diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 0bef18db33536..e459d1e55f616 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -2,6 +2,23 @@
/src/Symfony/Component/Console/Logger/ConsoleLogger.php @dunglas
# DependencyInjection
/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @dunglas
+# Form
+/src/Symfony/Bridge/Twig/Extension/FormExtension.php @xabbuh
+/src/Symfony/Bridge/Twig/Form/* @xabbuh
+/src/Symfony/Bridge/Twig/Node/FormThemeNode.php @xabbuh
+/src/Symfony/Bridge/Twig/Node/RenderBlockNode.php @xabbuh
+/src/Symfony/Bridge/Twig/Node/SearchAndRenderBlockNode.php @xabbuh
+/src/Symfony/Bridge/Twig/Tests/Extension/FormExtension* @xabbuh
+/src/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php @xabbuh
+/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php @xabbuh
+/src/Symfony/Bridge/Twig/TokenParser/FormThemeTokenParser.php @xabbuh
+/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/FormPass.php @xabbuh
+/src/Symfony/Bundle/FrameworkBundle/Resources/views/* @xabbuh
+/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/FormHelper.php @xabbuh
+/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/FormPassTest.php @xabbuh
+/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php @xabbuh
+/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php @xabbuh
+/src/Symfony/Component/Form/* @xabbuh
# HttpKernel
/src/Symfony/Component/HttpKernel/Log/Logger.php @dunglas
# LDAP
diff --git a/CHANGELOG-3.4.md b/CHANGELOG-3.4.md
index 8dabce2aaf90e..20ddc15fe2747 100644
--- a/CHANGELOG-3.4.md
+++ b/CHANGELOG-3.4.md
@@ -7,6 +7,41 @@ 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.23 (2019-03-03)
+
+ * bug #26532 [HttpKernel] Correctly merging cache directives in HttpCache/ResponseCacheStrategy (aschempp)
+ * bug #30363 Fixed the DebugClassLoader compatibility with eval()'d code on Darwin (skalpa)
+ * bug #30329 [Form] IntegerType: reject submitted non-integer numbers (xabbuh)
+ * bug #30347 [Security] Change FormAuthenticator if condition (PReimers)
+ * bug #30354 [Console] handles multi-byte characters in autocomplete (jls-esokia)
+ * bug #30351 Fix getItems() performance issue with RedisCluster (php-redis) (andrerom)
+ * bug #30350 [VarDumper] Keep a ref to objects to ensure their handle cannot be reused while cloning (nicolas-grekas)
+ * bug #30327 [HttpKernel] Fix possible infinite loop of exceptions (enumag)
+ * bug #27601 [Routing] fix URL generation with look-around requirements (nasimnabavi)
+ * bug #30277 [Console] Prevent ArgvInput::getFirstArgument() from returning an option value (chalasr)
+ * bug #29981 [Security] Complain about an empty decision strategy (corphi)
+ * bug #29822 [EventDispatcher] Fix unknown priority (ro0NL)
+ * bug #30324 [Validator] Fixed duplicate UUID (ralfkuehnel)
+ * bug #30265 [Form] do not validate non-submitted form fields in PATCH requests (xabbuh)
+ * bug #30313 Avoid mutating the Finder when building the iterator (stof)
+ * bug #30271 [Console] Fix command testing with missing user inputs (chalasr)
+ * bug #30278 Remove unnecessary ProgressBar stdout writes (fixes flickering) (ostrolucky)
+ * bug #30274 [VarDumper] fix serializing Stub instances (nicolas-grekas)
+ * bug #30247 Don't resolve the Deprecation error handler mode until a deprecation is triggered (ossinkine)
+ * bug #30264 [Debug][ErrorHandler] Preserve next error handler (fancyweb)
+ * bug #30090 [FrameworkBundle] add constraint validators before optimizations (xabbuh)
+ * feature #30126 [Form] forward valid numeric values to transform() (xabbuh)
+ * bug #30122 [Security] fix switch user without having current token (Antoine Lamirault)
+ * bug #30136 use PropertyAccessorInterface instead of PropertyAccessor (nick-zh)
+ * bug #30124 Fix KernelTestCase compatibility for PhpUnit 8 (bis) (nicolas-grekas)
+ * bug #30061 [Form] render integer types with grouping as text input (xabbuh)
+ * bug #30063 [Form] don't lose int precision with not needed type casts (xabbuh)
+ * bug #30076 [Form] ignore _method forms in NativeRequestHandler (xabbuh)
+ * bug #30084 Fix KernelTestCase compatibility for PhpUnit 8 (alexander-schranz)
+ * bug #29884 [Form] CsrfValidationListener marks the token as invalid if it is not a string (umpirsky)
+ * bug #30062 [Form] do not overwrite the constraint being evaluated (xabbuh)
+ * bug #30087 [PhpUnitBridge] fix PHP 5.3 compat (nicolas-grekas)
+
* 3.4.22 (2019-02-03)
* bug #30046 [DI] Fix dumping Doctrine-like service graphs (nicolas-grekas)
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index b6b40057db1df..bc64e2f7979e5 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -17,9 +17,9 @@ Symfony is the result of the work of many people who made the code better
- Maxime Steinhausser (ogizanagi)
- Jakub Zalas (jakubzalas)
- Johannes S (johannes)
+ - Javier Eguiluz (javier.eguiluz)
- Ryan Weaver (weaverryan)
- Kris Wallsmith (kriswallsmith)
- - Javier Eguiluz (javier.eguiluz)
- Roland Franssen (ro0)
- Grégoire Pineau (lyrixx)
- Hugo Hamon (hhamon)
@@ -36,8 +36,8 @@ Symfony is the result of the work of many people who made the code better
- Jean-François Simon (jfsimon)
- Benjamin Eberlei (beberlei)
- Igor Wiedler (igorw)
- - Jules Pietri (heah)
- Yonel Ceruto (yonelceruto)
+ - Jules Pietri (heah)
- Eriksen Costa (eriksencosta)
- Guilhem Niot (energetick)
- Sarah Khalil (saro0h)
@@ -56,8 +56,8 @@ Symfony is the result of the work of many people who made the code better
- Bulat Shakirzyanov (avalanche123)
- Matthias Pigulla (mpdude)
- Jérémy DERUSSÉ (jderusse)
- - Peter Rehm (rpet)
- Saša Stamenković (umpirsky)
+ - Peter Rehm (rpet)
- Kevin Bond (kbond)
- Pierre du Plessis (pierredup)
- Henrik Bjørnskov (henrikbjorn)
@@ -71,11 +71,11 @@ Symfony is the result of the work of many people who made the code better
- Gábor Egyed (1ed)
- Titouan Galopin (tgalopin)
- Vladimir Reznichenko (kalessil)
+ - Jáchym Toušek (enumag)
- Michel Weimerskirch (mweimerskirch)
- Andrej Hudec (pulzarraider)
- Konstantin Myakshin (koc)
- Eric Clemmons (ericclemmons)
- - Jáchym Toušek (enumag)
- Charles Sarrazin (csarrazi)
- David Maicher (dmaicher)
- Christian Raue
@@ -125,6 +125,7 @@ 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)
- Javier Spagnoletti (phansys)
- Fabien Pennequin (fabienpennequin)
- Eric GELOEN (gelo)
@@ -144,7 +145,6 @@ Symfony is the result of the work of many people who made the code better
- Daniel Gomes (danielcsgomes)
- Gabriel Caruso
- Hidenori Goto (hidenorigoto)
- - Thomas Calvet (fancyweb)
- Arnaud Kleinpeter (nanocom)
- Jannik Zschiesche (apfelbox)
- Guilherme Blanco (guilhermeblanco)
@@ -181,6 +181,7 @@ 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)
@@ -195,7 +196,9 @@ 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)
- Benjamin Dulau (dbenjamin)
+ - François-Xavier de Guillebon (de-gui_f)
- Mathieu Lemoine (lemoinem)
- Christian Schmidt
- Andreas Hucks (meandmymonkey)
@@ -214,13 +217,13 @@ 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)
- - Oskar Stark (oskarstark)
- Leszek Prabucki (l3l0)
- Fabien Bourigault (fbourigault)
- François Zaninotto (fzaninotto)
@@ -239,7 +242,6 @@ Symfony is the result of the work of many people who made the code better
- Tristan Darricau (nicofuma)
- Marcel Beerta (mazen)
- Pavel Batanov (scaytrase)
- - Samuel NELA (snela)
- Loïc Faugeron
- Hidde Wieringa (hiddewie)
- Marco Pivetta (ocramius)
@@ -262,7 +264,6 @@ Symfony is the result of the work of many people who made the code better
- Benoît Burnichon (bburnichon)
- Roman Marintšenko (inori)
- Xavier Montaña Carreras (xmontana)
- - François-Xavier de Guillebon (de-gui_f)
- Mickaël Andrieu (mickaelandrieu)
- Maxime Veber (nek-)
- Xavier Perez
@@ -318,6 +319,7 @@ 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)
@@ -366,6 +368,7 @@ Symfony is the result of the work of many people who made the code better
- Thomas Royer (cydonia7)
- alquerci
- Francesco Levorato
+ - Pascal Luna (skalpa)
- Dmitrii Poddubnyi (karser)
- Vitaliy Zakharov (zakharovvi)
- Tobias Sjösten (tobiassjosten)
@@ -384,7 +387,6 @@ Symfony is the result of the work of many people who made the code better
- JhonnyL
- David Badura (davidbadura)
- hossein zolfi (ocean)
- - Alexander Schranz (alexander-schranz)
- Clément Gautier (clementgautier)
- Sanpi
- Eduardo Gulias (egulias)
@@ -393,6 +395,7 @@ Symfony is the result of the work of many people who made the code better
- Stéphane PY (steph_py)
- Philipp Kräutli (pkraeutli)
- Grzegorz (Greg) Zdanowski (kiler129)
+ - Iker Ibarguren (ikerib)
- Kirill chEbba Chebunin (chebba)
- Greg Thornton (xdissent)
- Martin Hujer (martinhujer)
@@ -447,8 +450,10 @@ Symfony is the result of the work of many people who made the code better
- Valentin Jonovs (valentins-jonovs)
- Jeanmonod David (jeanmonod)
- Christopher Davis (chrisguitarguy)
+ - Webnet team (webnet)
- Jan Schumann
- Niklas Fiekas
+ - renanbr
- Markus Bachmann (baachi)
- lancergr
- Mihai Stancu
@@ -456,7 +461,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)
- - Pascal Luna (skalpa)
+ - Andre Rømcke (andrerom)
- Arturs Vonda
- Josip Kruslin
- Asmir Mustafic (goetas)
@@ -480,6 +485,7 @@ Symfony is the result of the work of many people who made the code better
- Jonas Flodén (flojon)
- Gonzalo Vilaseca (gonzalovilaseca)
- Marcin Sikoń (marphi)
+ - Przemysław Bogusz (przemyslaw-bogusz)
- Dominik Zogg (dominik.zogg)
- Marek Pietrzak
- Luc Vieillescazes (iamluc)
@@ -492,6 +498,7 @@ Symfony is the result of the work of many people who made the code better
- Adam Harvey
- Anton Bakai
- Rhodri Pugh (rodnaph)
+ - Sam Fleming (sam_fleming)
- Alex Bakhturin
- insekticid
- Alexander Obuhovich (aik099)
@@ -516,7 +523,6 @@ Symfony is the result of the work of many people who made the code better
- Manuel de Ruiter (manuel)
- Eduardo Oliveira (entering)
- Ilya Antipenko (aivus)
- - Iker Ibarguren (ikerib)
- Ricardo Oliveira (ricardolotr)
- Roy Van Ginneken (rvanginneken)
- ondrowan
@@ -546,6 +552,7 @@ Symfony is the result of the work of many people who made the code better
- Almog Baku (almogbaku)
- Scott Arciszewski
- Xavier HAUSHERR
+ - Philipp Cordes
- Norbert Orzechowicz (norzechowicz)
- Denis Charrier (brucewouaigne)
- Matthijs van den Bos (matthijs)
@@ -596,6 +603,7 @@ Symfony is the result of the work of many people who made the code better
- Jan Behrens
- Mantas Var (mvar)
- Sebastian Krebs
+ - Baptiste Leduc (bleduc)
- Laurent VOULLEMIER (lvo)
- Jean-Christophe Cuvelier [Artack]
- Simon DELICATA
@@ -617,7 +625,6 @@ Symfony is the result of the work of many people who made the code better
- Sinan Eldem
- Alexandre Dupuy (satchette)
- Malte Blättermann
- - Andre Rømcke (andrerom)
- Nahuel Cuesta (ncuesta)
- Chris Boden (cboden)
- Christophe Villeger (seragan)
@@ -626,7 +633,6 @@ Symfony is the result of the work of many people who made the code better
- Stefan Gehrig (sgehrig)
- Hany el-Kerdany
- Wang Jingyu
- - Webnet team (webnet)
- Åsmund Garfors
- Gunnstein Lye (glye)
- Maxime Douailin
@@ -641,6 +647,7 @@ Symfony is the result of the work of many people who made the code better
- dantleech
- Anne-Sophie Bachelard (annesophie)
- Sebastian Marek (proofek)
+ - Guilhem N (guilhemn)
- Erkhembayar Gantulga (erheme318)
- Michal Trojanowski
- David Fuhr
@@ -656,6 +663,7 @@ Symfony is the result of the work of many people who made the code better
- Stefan Warman
- Arkadius Stefanski (arkadius)
- Tristan Maindron (tmaindron)
+ - Behnoush Norouzali (behnoush)
- Wesley Lancel
- Ke WANG (yktd26)
- Ivo Bathke (ivoba)
@@ -678,6 +686,7 @@ Symfony is the result of the work of many people who made the code better
- Sascha Grossenbacher
- Emanuele Panzeri (thepanz)
- Szijarto Tamas
+ - Gocha Ossinkine (ossinkine)
- Robin Lehrmann (robinlehrmann)
- Catalin Dan
- Jaroslav Kuba
@@ -734,10 +743,10 @@ Symfony is the result of the work of many people who made the code better
- Pascal Helfenstein
- Anthony GRASSIOT (antograssiot)
- Baldur Rensch (brensch)
- - Anthony MARTIN (xurudragon)
- Pierre Rineau
- Vladyslav Petrovych
- Alex Xandra Albert Sim
+ - Patrick Landolt (scube)
- Carson Full
- Sergey Yastrebov
- Trent Steel (trsteel88)
@@ -774,6 +783,7 @@ Symfony is the result of the work of many people who made the code better
- Noah Heck (myesain)
- Christian Soronellas (theunic)
- Johann Pardanaud
+ - fedor.f
- Yosmany Garcia (yosmanyga)
- Wouter de Wild
- Antoine M (amakdessi)
@@ -786,9 +796,9 @@ Symfony is the result of the work of many people who made the code better
- possum
- Denis Zunke (donalberto)
- Ahmadou Waly Ndiaye (waly)
- - Philipp Cordes
- Ahmed TAILOULOUTE (ahmedtai)
- Olivier Maisonneuve (olineuve)
+ - Pedro Miguel Maymone de Resende (pedroresende)
- Masterklavi
- Francis Turmel (fturmel)
- Nikita Nefedov (nikita2206)
@@ -803,6 +813,7 @@ Symfony is the result of the work of many people who made the code better
- Harm van Tilborg
- Jan Prieser
- GDIBass
+ - Antoine Lamirault
- Adrien Lucas (adrienlucas)
- Zhuravlev Alexander (scif)
- James Michael DuPont
@@ -899,7 +910,6 @@ Symfony is the result of the work of many people who made the code better
- Adán Lobato (adanlobato)
- Ian Jenkins (jenkoian)
- Matthew Davis (mdavis1982)
- - Sam Fleming (sam_fleming)
- Maks
- Antoine LA
- den
@@ -907,6 +917,7 @@ Symfony is the result of the work of many people who made the code better
- omerida
- Gábor Tóth
- Daniel Cestari
+ - Matt Janssen
- David Lima
- Stéphane Delprat
- Brian Freytag (brianfreytag)
@@ -926,7 +937,6 @@ Symfony is the result of the work of many people who made the code better
- Kyle
- Daniel Alejandro Castro Arellano (lexcast)
- sensio
- - Baptiste Leduc (bleduc)
- Sebastien Morel (plopix)
- Patrick Kaufmann
- Piotr Stankowski
@@ -974,10 +984,10 @@ Symfony is the result of the work of many people who made the code better
- Sander Coolen (scoolen)
- Nicolas Le Goff (nlegoff)
- Ben Oman
- - Guilhem N (guilhemn)
- Chris de Kok
- Andreas Kleemann
- Manuele Menozzi
+ - zairig imad (zairigimad)
- Anton Babenko (antonbabenko)
- Irmantas Šiupšinskas (irmantas)
- Danilo Silva
@@ -1013,23 +1023,25 @@ Symfony is the result of the work of many people who made the code better
- tamirvs
- julien.galenski
- Christian Neff
+ - Chris Tiearney
- Oliver Hoff
- Ole Rößner (basster)
+ - Faton (notaf)
+ - Tom Houdmont
- Per Sandström (per)
- Goran Juric
- Laurent Ghirardotti (laurentg)
- Nicolas Macherey
- Guido Donnari
- AKeeman (akeeman)
+ - Mert Simsek (mrtsmsk0)
- Lin Clark
- Jeremy David (jeremy.david)
- - Gocha Ossinkine (ossinkine)
- Troy McCabe
- Ville Mattila
- ilyes kooli
- gr1ev0us
- mlazovla
- - Behnoush norouzali (behnoush)
- Max Beutel
- Antanas Arvasevicius
- Pierre Dudoret
@@ -1038,6 +1050,7 @@ Symfony is the result of the work of many people who made the code better
- nacho
- Piotr Antosik (antek88)
- Artem Lopata
+ - Patrick Reimers (preimers)
- Sergey Novikov (s12v)
- Marcos Quesada (marcos_quesada)
- Matthew Vickery (mattvick)
@@ -1052,6 +1065,7 @@ 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)
@@ -1060,6 +1074,7 @@ Symfony is the result of the work of many people who made the code better
- Denis Golubovskiy (bukashk0zzz)
- Sergii Smertin (nfx)
- Michał Strzelecki
+ - Soner Sayakci
- hugofonseca (fonsecas72)
- Martynas Narbutas
- Toon Verwerft (veewee)
@@ -1133,10 +1148,13 @@ 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
- fduch (fduch)
- David de Boer (ddeboer)
+ - Eno Mullaraj (emullaraj)
- Ryan Rogers
- Klaus Purer
- arnaud (arnooo999)
@@ -1155,7 +1173,6 @@ Symfony is the result of the work of many people who made the code better
- Max Voloshin (maxvoloshin)
- Nicolas Fabre (nfabre)
- Raul Rodriguez (raul782)
- - Patrick Landolt (scube)
- WybrenKoelmans
- Derek Lambert
- MightyBranch
@@ -1183,6 +1200,7 @@ Symfony is the result of the work of many people who made the code better
- Dmitri Petmanson
- heccjj
- Alexandre Melard
+ - Jonathan (jls-esokia)
- Jay Klehr
- Sergey Yuferev
- Tobias Stöckler
@@ -1244,6 +1262,7 @@ Symfony is the result of the work of many people who made the code better
- Sébastien HOUZÉ
- Jingyu Wang
- steveYeah
+ - BENOIT POLASZEK (bpolaszek)
- Samy Dindane (dinduks)
- Keri Henare (kerihenare)
- Cédric Lahouste (rapotor)
@@ -1252,7 +1271,6 @@ Symfony is the result of the work of many people who made the code better
- Berat Doğan
- Guillaume LECERF
- Juanmi Rodriguez Cerón
- - renanbr
- Andy Raines
- Anthony Ferrara
- Geoffrey Pécro (gpekz)
@@ -1277,6 +1295,7 @@ Symfony is the result of the work of many people who made the code better
- Luis Ramirez (luisdeimos)
- Daniel Richter (richtermeister)
- ChrisC
+ - JL
- Ilya Biryukov
- Kim Laï Trinh
- Jason Desrosiers
@@ -1311,11 +1330,14 @@ Symfony is the result of the work of many people who made the code better
- Adrien Moiruad
- Tomaz Ahlin
- Philip Ardery
+ - Nasim
- Marcus Stöhr (dafish)
- Daniel González Zaballos (dem3trio)
- Emmanuel Vella (emmanuel.vella)
+ - Guillaume BRETOU (guiguiboy)
- Jonathan Johnson (jrjohnson)
- Carsten Nielsen (phreaknerd)
+ - Roger Guasch (rogerguasch)
- Mathieu Rochette
- Jay Severson
- René Kerner
@@ -1343,11 +1365,14 @@ Symfony is the result of the work of many people who made the code better
- Thomas Counsell
- BilgeXA
- r1pp3rj4ck
+ - phydevs
- Robert Queck
- Peter Bouwdewijn
- mlively
- Amine Matmati
+ - caalholm
- Fabian Steiner (fabstei)
+ - Felipy Tavares Amorim (felipyamorim)
- Klaus Silveira (klaussilveira)
- Thomas Chmielowiec (chmielot)
- Jānis Lukss
@@ -1393,7 +1418,9 @@ Symfony is the result of the work of many people who made the code better
- Keith Maika
- Mephistofeles
- Hoffmann András
+ - LubenZA
- Olivier
+ - Anton Chernikov
- Cyril PASCAL
- pscheit
- Wybren Koelmans
@@ -1402,8 +1429,10 @@ Symfony is the result of the work of many people who made the code better
- moldcraft
- Antoine Bellion (abellion)
- Ramon Kleiss (akathos)
+ - Antonio Peric-Mazar (antonioperic)
- César Suárez (csuarez)
- Bjorn Twachtmann (dotbjorn)
+ - Luis Tacón (lutacon)
- Nicolas Badey (nico-b)
- Shane Preece (shane)
- Johannes Goslar
@@ -1436,12 +1465,14 @@ 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
- Francisco Facioni (fran6co)
- Iwan van Staveren (istaveren)
- Povilas S. (povilas)
+ - Evrard Boulou
- pborreli
- Boris Betzholz
- Eric Caron
@@ -1452,10 +1483,13 @@ Symfony is the result of the work of many people who made the code better
- catch
- Alexandre Segura
- Josef Cech
+ - Andrii Boiko
- Harold Iedema
+ - Ikhsan Agustian
- Arnau González (arnaugm)
- Simon Bouland (bouland)
- Matthew Foster (mfoster)
+ - Reyo Stallenberg (reyostallenberg)
- Paul Seiffert (seiffert)
- Vasily Khayrulin (sirian)
- Stefan Koopmanschap (skoop)
@@ -1536,9 +1570,11 @@ Symfony is the result of the work of many people who made the code better
- Normunds
- Luiz “Felds” Liscia
- Thomas Rothe
+ - Martin
- nietonfir
- alefranz
- David Barratt
+ - Andrea Giannantonio
- Pavel.Batanov
- avi123
- Pavel Prischepa
@@ -1559,12 +1595,14 @@ Symfony is the result of the work of many people who made the code better
- Nicolas Pion
- Muhammed Akbulut
- Aaron Somi
+ - Karoly Gossler (connorhu)
- Michał Dąbrowski (defrag)
- Konstantin Grachev (grachevko)
- Simone Fumagalli (hpatoio)
- Brian Graham (incognito)
- Kevin Vergauwen (innocenzo)
- Alessio Baglio (ioalessio)
+ - Jan van Thoor (janvt)
- Johannes Müller (johmue)
- Jordi Llonch (jordillonch)
- Cédric Dugat (ph3nol)
@@ -1680,6 +1718,7 @@ Symfony is the result of the work of many people who made the code better
- Jonny Schmid (schmidjon)
- Götz Gottwald
- Veres Lajos
+ - Nick Chiu
- grifx
- Robert Campbell
- Matt Lehner
@@ -1714,6 +1753,7 @@ Symfony is the result of the work of many people who made the code better
- skafandri
- Derek Bonner
- Alan Chen
+ - insidestyles
- Maerlyn
- Even André Fiskvik
- Arjan Keeman
@@ -1750,8 +1790,10 @@ Symfony is the result of the work of many people who made the code better
- Brieuc THOMAS (brieucthomas)
- Masao Maeda (brtriver)
- Darius Leskauskas (darles)
+ - david perez (davidpv)
- David Joos (djoos)
- Denis Klementjev (dklementjev)
+ - Dominik Hajduk (dominikalp)
- Tomáš Polívka (draczris)
- Dennis Smink (dsmink)
- Franz Liedke (franzliedke)
@@ -1932,13 +1974,14 @@ Symfony is the result of the work of many people who made the code better
- zorn
- Yuriy Potemkin
- Emilie Lorenzo
+ - enomotodev
- Edvin Hultberg
- Benjamin Long
- - Matt Janssen
- Ben Miller
- Peter Gribanov
- kwiateusz
- jspee
+ - Ilya Bulakh
- David Soria Parra
- Sergiy Sokolenko
- Ahmed Abdulrahman
@@ -2042,10 +2085,10 @@ Symfony is the result of the work of many people who made the code better
- Tomas Norkūnas (norkunas)
- Marco Petersen (ocrampete16)
- ollie harridge (ollietb)
+ - Dimitri Gritsajuk (ottaviano)
- Paul Andrieux (paulandrieux)
- Paweł Szczepanek (pauluz)
- Philippe Degeeter (pdegeeter)
- - Pedro Miguel Maymone de Resende (pedroresende)
- Christian López Espínola (penyaskito)
- Petr Jaroš (petajaros)
- Philipp Hoffmann (philipphoffmann)
@@ -2053,6 +2096,7 @@ Symfony is the result of the work of many people who made the code better
- Daniel Perez Pinazo (pitiflautico)
- Phil Taylor (prazgod)
- Maxim Pustynnikov (pustynnikov)
+ - Ralf Kuehnel (ralfkuehnel)
- Brayden Williams (redstar504)
- Rich Sage (richsage)
- Rokas Mikalkėnas (rokasm)
@@ -2113,6 +2157,7 @@ Symfony is the result of the work of many people who made the code better
- Andrew Carter (andrewcarteruk)
- Adam Elsodaney (archfizz)
- Gregório Bonfante Borba (bonfante)
+ - Bogdan Rancichi (devck)
- Daniel Kolvik (dkvk)
- Marc Lemay (flug)
- Henne Van Och (hennevo)
diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md
index fe43d0cf99737..fbe988fa3497f 100644
--- a/UPGRADE-4.0.md
+++ b/UPGRADE-4.0.md
@@ -230,7 +230,7 @@ DependencyInjection
supported.
* The ``strict`` attribute in service arguments has been removed.
- The attribute is ignored since 3.0, so you can simply remove it.
+ The attribute is ignored since 3.0, you can remove it.
* Top-level anonymous services in XML are no longer supported.
diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php
index 860a6c7c20d0a..7ac1856114d7e 100644
--- a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php
+++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php
@@ -24,8 +24,8 @@
* The compiler pass is meant to register the mappings with the metadata
* chain driver corresponding to one of the object managers.
*
- * For concrete implementations that are easy to use, see the
- * RegisterXyMappingsPass classes in the DoctrineBundle resp.
+ * For concrete implementations, see the RegisterXyMappingsPass classes
+ * in the DoctrineBundle resp.
* DoctrineMongodbBundle, DoctrineCouchdbBundle and DoctrinePhpcrBundle.
*
* @author David Buchmann
diff --git a/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php b/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php
index 689101d2e09a6..e4110f1d785f8 100644
--- a/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php
+++ b/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php
@@ -157,7 +157,7 @@ protected function getMetadata($class)
// normalize class name
$class = ClassUtils::getRealClass(ltrim($class, '\\'));
- if (array_key_exists($class, $this->cache)) {
+ if (\array_key_exists($class, $this->cache)) {
return $this->cache[$class];
}
diff --git a/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php b/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php
index 8923aa21aa29f..7c630b4e98399 100644
--- a/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php
+++ b/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php
@@ -20,7 +20,7 @@
/**
* Wrapper around a Doctrine ObjectManager.
*
- * Provides easy to use provisioning for Doctrine entity users.
+ * Provides provisioning for Doctrine entity users.
*
* @author Fabien Potencier
* @author Johannes M. Schmitt
diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php
index afdeb5f720e27..5012171542f7b 100644
--- a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php
+++ b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php
@@ -30,6 +30,8 @@ class EntityTypePerformanceTest extends FormPerformanceTestCase
*/
private $em;
+ protected static $supportedFeatureSetVersion = 304;
+
protected function getExtensions()
{
$manager = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')->getMock();
diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php
index 2869c2b804c4c..1cb59c38436ef 100644
--- a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php
+++ b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php
@@ -57,6 +57,8 @@ class EntityTypeTest extends BaseTypeTest
*/
private $emRegistry;
+ protected static $supportedFeatureSetVersion = 304;
+
protected function setUp()
{
$this->em = DoctrineTestHelper::createTestEntityManager();
diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php
index 3b444d5d95b2a..fc60cbd4365be 100644
--- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php
+++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php
@@ -54,9 +54,9 @@ public static function register($mode = 0)
if (false === $mode) {
$mode = getenv('SYMFONY_DEPRECATIONS_HELPER');
}
- if (self::MODE_DISABLED !== $mode
- && self::MODE_WEAK !== $mode
- && self::MODE_WEAK_VENDORS !== $mode
+ if (DeprecationErrorHandler::MODE_DISABLED !== $mode
+ && DeprecationErrorHandler::MODE_WEAK !== $mode
+ && DeprecationErrorHandler::MODE_WEAK_VENDORS !== $mode
&& (!isset($mode[0]) || '/' !== $mode[0])
) {
$mode = preg_match('/^[1-9][0-9]*$/', $mode) ? (int) $mode : 0;
@@ -105,8 +105,7 @@ public static function register($mode = 0)
'remaining vendor' => array(),
);
$deprecationHandler = function ($type, $msg, $file, $line, $context = array()) use (&$deprecations, $getMode, $UtilPrefix, $inVendors) {
- $mode = $getMode();
- if ((E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) || self::MODE_DISABLED === $mode) {
+ if ((E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) || DeprecationErrorHandler::MODE_DISABLED === $mode = $getMode()) {
$ErrorHandler = $UtilPrefix.'ErrorHandler';
return $ErrorHandler::handleError($type, $msg, $file, $line, $context);
@@ -114,7 +113,7 @@ public static function register($mode = 0)
$trace = debug_backtrace();
$group = 'other';
- $isVendor = self::MODE_WEAK_VENDORS === $mode && $inVendors($file);
+ $isVendor = DeprecationErrorHandler::MODE_WEAK_VENDORS === $mode && $inVendors($file);
$i = \count($trace);
while (1 < $i && (!isset($trace[--$i]['class']) || ('ReflectionMethod' === $trace[$i]['class'] || 0 === strpos($trace[$i]['class'], 'PHPUnit_') || 0 === strpos($trace[$i]['class'], 'PHPUnit\\')))) {
@@ -131,7 +130,7 @@ public static function register($mode = 0)
// \Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait::endTest()
// then we need to use the serialized information to determine
// if the error has been triggered from vendor code.
- $isVendor = self::MODE_WEAK_VENDORS === $mode && isset($parsedMsg['triggering_file']) && $inVendors($parsedMsg['triggering_file']);
+ $isVendor = DeprecationErrorHandler::MODE_WEAK_VENDORS === $mode && isset($parsedMsg['triggering_file']) && $inVendors($parsedMsg['triggering_file']);
} else {
$class = isset($trace[$i]['object']) ? \get_class($trace[$i]['object']) : $trace[$i]['class'];
$method = $trace[$i]['function'];
@@ -168,13 +167,13 @@ public static function register($mode = 0)
exit(1);
}
- if ('legacy' !== $group && self::MODE_WEAK !== $mode) {
+ if ('legacy' !== $group && DeprecationErrorHandler::MODE_WEAK !== $mode) {
$ref = &$deprecations[$group][$msg]['count'];
++$ref;
$ref = &$deprecations[$group][$msg][$class.'::'.$method];
++$ref;
}
- } elseif (self::MODE_WEAK !== $mode) {
+ } elseif (DeprecationErrorHandler::MODE_WEAK !== $mode) {
$ref = &$deprecations[$group][$msg]['count'];
++$ref;
}
@@ -207,7 +206,7 @@ public static function register($mode = 0)
$currErrorHandler = set_error_handler('var_dump');
restore_error_handler();
- if (self::MODE_WEAK === $mode) {
+ if (DeprecationErrorHandler::MODE_WEAK === $mode) {
$colorize = function ($str) { return $str; };
}
if ($currErrorHandler !== $deprecationHandler) {
@@ -219,7 +218,7 @@ public static function register($mode = 0)
};
$groups = array('unsilenced', 'remaining');
- if (self::MODE_WEAK_VENDORS === $mode) {
+ if (DeprecationErrorHandler::MODE_WEAK_VENDORS === $mode) {
$groups[] = 'remaining vendor';
}
array_push($groups, 'legacy', 'other');
@@ -255,7 +254,7 @@ public static function register($mode = 0)
$displayDeprecations($deprecations);
// store failing status
- $isFailing = self::MODE_WEAK !== $mode && $mode < $deprecations['unsilencedCount'] + $deprecations['remainingCount'] + $deprecations['otherCount'];
+ $isFailing = DeprecationErrorHandler::MODE_WEAK !== $mode && $mode < $deprecations['unsilencedCount'] + $deprecations['remainingCount'] + $deprecations['otherCount'];
// reset deprecations array
foreach ($deprecations as $group => $arrayOrInt) {
@@ -270,7 +269,7 @@ public static function register($mode = 0)
}
}
$displayDeprecations($deprecations);
- if ($isFailing || self::MODE_WEAK !== $mode && $mode < $deprecations['unsilencedCount'] + $deprecations['remainingCount'] + $deprecations['otherCount']) {
+ if ($isFailing || DeprecationErrorHandler::MODE_WEAK !== $mode && $mode < $deprecations['unsilencedCount'] + $deprecations['remainingCount'] + $deprecations['otherCount']) {
exit(1);
}
});
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/default.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/default.phpt
index e06e9bd869aca..7a0595a7ddebc 100644
--- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/default.phpt
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/default.phpt
@@ -25,7 +25,7 @@ class Test
{
public static function getGroups()
{
- return [];
+ return array();
}
}
EOPHP
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/acme/lib/deprecation_riddled.php b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/acme/lib/deprecation_riddled.php
index 5229a7a7cc6f5..16a58246d2115 100644
--- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/acme/lib/deprecation_riddled.php
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/acme/lib/deprecation_riddled.php
@@ -7,7 +7,7 @@ class Test
{
public static function getGroups()
{
- return [];
+ return array();
}
}
EOPHP
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/shutdown_deprecations.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/shutdown_deprecations.phpt
index 1bb1551663e13..fddeed6085dea 100644
--- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/shutdown_deprecations.phpt
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/shutdown_deprecations.phpt
@@ -25,7 +25,7 @@ class Test
{
public static function getGroups()
{
- return [];
+ return array();
}
}
EOPHP
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_non_vendor.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_non_vendor.phpt
index b37b623cf297a..e20c7adf6ba1f 100644
--- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_non_vendor.phpt
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_non_vendor.phpt
@@ -25,7 +25,7 @@ class Test
{
public static function getGroups()
{
- return [];
+ return array();
}
}
EOPHP
diff --git a/src/Symfony/Bridge/PhpUnit/composer.json b/src/Symfony/Bridge/PhpUnit/composer.json
index 24a48b600d1b5..25dcb0cd10272 100644
--- a/src/Symfony/Bridge/PhpUnit/composer.json
+++ b/src/Symfony/Bridge/PhpUnit/composer.json
@@ -21,8 +21,7 @@
"php": ">=5.3.3"
},
"suggest": {
- "symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader",
- "ext-zip": "Zip support is required when using bin/simple-phpunit"
+ "symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader"
},
"conflict": {
"phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0"
diff --git a/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php b/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php
index 04b8dda567307..80e36e0491efc 100644
--- a/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php
+++ b/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php
@@ -16,6 +16,7 @@
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
use Twig\Environment;
+use Twig\Error\LoaderError;
use Twig\Markup;
use Twig\Profiler\Dumper\HtmlDumper;
use Twig\Profiler\Profile;
@@ -70,7 +71,7 @@ public function lateCollect()
if ($profile->isTemplate()) {
try {
$template = $this->twig->load($name = $profile->getName());
- } catch (\Twig_Error_Loader $e) {
+ } catch (LoaderError $e) {
$template = null;
}
diff --git a/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php b/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php
index d5bcfa8b8fceb..642623f2a693c 100644
--- a/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php
+++ b/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php
@@ -77,7 +77,7 @@ public function set($key, $value)
*/
public function has($key)
{
- if (array_key_exists($key, $this->data)) {
+ if (\array_key_exists($key, $this->data)) {
return true;
}
@@ -98,7 +98,7 @@ public function has($key)
*/
public function get($key, $default = null)
{
- if (array_key_exists($key, $this->data)) {
+ if (\array_key_exists($key, $this->data)) {
return $this->data[$key];
}
diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTest.php
index ebfb105826b33..e50076da73105 100644
--- a/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTest.php
+++ b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTest.php
@@ -16,6 +16,8 @@
abstract class AbstractBootstrap3LayoutTest extends AbstractLayoutTest
{
+ protected static $supportedFeatureSetVersion = 304;
+
public function testLabelOnForm()
{
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType');
@@ -1863,6 +1865,22 @@ public function testInteger()
);
}
+ public function testIntegerTypeWithGroupingRendersAsTextInput()
+ {
+ $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\IntegerType', 123, [
+ 'grouping' => true,
+ ]);
+
+ $this->assertWidgetMatchesXpath($form->createView(), ['attr' => ['class' => 'my&class']],
+'/input
+ [@type="text"]
+ [@name="name"]
+ [@class="my&class form-control"]
+ [@value="123"]
+'
+ );
+ }
+
public function testLanguage()
{
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\LanguageType', 'de');
diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php
index 456e6730faa34..214df3c7f6b18 100644
--- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php
+++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php
@@ -31,6 +31,8 @@ class FormExtensionDivLayoutTest extends AbstractDivLayoutTest
*/
private $renderer;
+ protected static $supportedFeatureSetVersion = 304;
+
protected function setUp()
{
parent::setUp();
diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php
index 7811a41eb6bb7..f956767363a97 100644
--- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php
+++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php
@@ -30,6 +30,8 @@ class FormExtensionTableLayoutTest extends AbstractTableLayoutTest
*/
private $renderer;
+ protected static $supportedFeatureSetVersion = 304;
+
protected function setUp()
{
parent::setUp();
diff --git a/src/Symfony/Bridge/Twig/Tests/Node/TransNodeTest.php b/src/Symfony/Bridge/Twig/Tests/Node/TransNodeTest.php
index 84a1ad8a938d0..3008f43f6f4bc 100644
--- a/src/Symfony/Bridge/Twig/Tests/Node/TransNodeTest.php
+++ b/src/Symfony/Bridge/Twig/Tests/Node/TransNodeTest.php
@@ -54,7 +54,7 @@ protected function getVariableGetterWithoutStrictCheck($name)
protected function getVariableGetterWithStrictCheck($name)
{
if (Environment::MAJOR_VERSION >= 2) {
- return sprintf('(isset($context["%s"]) || array_key_exists("%1$s", $context) ? $context["%1$s"] : (function () { throw new Twig_Error_Runtime(\'Variable "%1$s" does not exist.\', 0, $this->source); })())', $name);
+ return sprintf('(isset($context["%1$s"]) || array_key_exists("%1$s", $context) ? $context["%1$s"] : (function () { throw new %2$s(\'Variable "%1$s" does not exist.\', 0, $this->source); })())', $name, Environment::VERSION_ID >= 20700 ? 'RuntimeError' : 'Twig_Error_Runtime');
}
if (\PHP_VERSION_ID >= 70000) {
diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json
index 58e90971bec11..42d8bef4ace45 100644
--- a/src/Symfony/Bridge/Twig/composer.json
+++ b/src/Symfony/Bridge/Twig/composer.json
@@ -23,7 +23,7 @@
"symfony/asset": "~2.8|~3.0|~4.0",
"symfony/dependency-injection": "~2.8|~3.0|~4.0",
"symfony/finder": "~2.8|~3.0|~4.0",
- "symfony/form": "^3.4.22|~4.1.11|^4.2.3",
+ "symfony/form": "^3.4.23|^4.2.4",
"symfony/http-foundation": "^3.3.11|~4.0",
"symfony/http-kernel": "~3.2|~4.0",
"symfony/polyfill-intl-icu": "~1.0",
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php
index 758c605ecfa01..6aa3fbf9b7183 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php
@@ -139,7 +139,7 @@ private function getConfigForPath(array $config, $path, $alias)
$steps = explode('.', $path);
foreach ($steps as $step) {
- if (!array_key_exists($step, $config)) {
+ if (!\array_key_exists($step, $config)) {
throw new LogicException(sprintf('Unable to find configuration for "%s.%s"', $alias, $path));
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php
index fda0fb28d3039..c7b669fda0228 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php
@@ -225,7 +225,7 @@ protected function resolveServiceDefinition(ContainerBuilder $builder, $serviceI
return $builder->getDefinition($serviceId);
}
- // Some service IDs don't have a Definition, they're simply an Alias
+ // Some service IDs don't have a Definition, they're aliases
if ($builder->hasAlias($serviceId)) {
return $builder->getAlias($serviceId);
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php
index 83f607cc384f6..6b05612ff53d9 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php
@@ -156,7 +156,7 @@ protected function describeContainerAlias(Alias $alias, array $options = [], Con
*/
protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = [])
{
- $this->writeData($this->getEventDispatcherListenersData($eventDispatcher, array_key_exists('event', $options) ? $options['event'] : null), $options);
+ $this->writeData($this->getEventDispatcherListenersData($eventDispatcher, \array_key_exists('event', $options) ? $options['event'] : null), $options);
}
/**
diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php
index a0a0dc85166fe..6575b05ec81e8 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php
@@ -275,7 +275,7 @@ protected function describeContainerParameter($parameter, array $options = [])
*/
protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = [])
{
- $event = array_key_exists('event', $options) ? $options['event'] : null;
+ $event = \array_key_exists('event', $options) ? $options['event'] : null;
$title = 'Registered listeners';
if (null !== $event) {
diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php
index 7aed45bd4aced..90c7602604e2e 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php
@@ -385,7 +385,7 @@ protected function describeContainerParameter($parameter, array $options = [])
*/
protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = [])
{
- $event = array_key_exists('event', $options) ? $options['event'] : null;
+ $event = \array_key_exists('event', $options) ? $options['event'] : null;
if (null !== $event) {
$title = sprintf('Registered Listeners for "%s" Event', $event);
diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php
index 38efe85753542..53e2ee1fac358 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php
@@ -111,7 +111,7 @@ protected function describeContainerAlias(Alias $alias, array $options = [], Con
*/
protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = [])
{
- $this->writeDocument($this->getEventDispatcherListenersDocument($eventDispatcher, array_key_exists('event', $options) ? $options['event'] : null));
+ $this->writeDocument($this->getEventDispatcherListenersDocument($eventDispatcher, \array_key_exists('event', $options) ? $options['event'] : null));
}
/**
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
index f49c5f11e0eec..52c7706456c9f 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
@@ -371,7 +371,7 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode)
}
foreach ($transitions as $name => $transition) {
- if (array_key_exists('name', $transition)) {
+ if (\array_key_exists('name', $transition)) {
continue;
}
$transition['name'] = $name;
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
index a46e552a255e3..ee5dc86df201f 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -602,7 +602,7 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $
$registryDefinition = $container->getDefinition('workflow.registry');
foreach ($config['workflows'] as $name => $workflow) {
- if (!array_key_exists('type', $workflow)) {
+ if (!\array_key_exists('type', $workflow)) {
$workflow['type'] = 'workflow';
@trigger_error(sprintf('The "type" option of the "framework.workflows.%s" configuration entry must be defined since Symfony 3.3. The default value will be "state_machine" in Symfony 4.0.', $name), E_USER_DEPRECATED);
}
@@ -1052,7 +1052,7 @@ private function registerAssetsConfiguration(array $config, ContainerBuilder $co
foreach ($config['packages'] as $name => $package) {
if (null !== $package['version_strategy']) {
$version = new Reference($package['version_strategy']);
- } elseif (!array_key_exists('version', $package) && null === $package['json_manifest_path']) {
+ } elseif (!\array_key_exists('version', $package) && null === $package['json_manifest_path']) {
// if neither version nor json_manifest_path are specified, use the default
$version = $defaultVersion;
} else {
@@ -1273,7 +1273,7 @@ private function registerValidationConfiguration(array $config, ContainerBuilder
$definition = $container->findDefinition('validator.email');
$definition->replaceArgument(0, $config['strict_email']);
- if (array_key_exists('enable_annotations', $config) && $config['enable_annotations']) {
+ if (\array_key_exists('enable_annotations', $config) && $config['enable_annotations']) {
if (!$this->annotationsConfigEnabled) {
throw new \LogicException('"enable_annotations" on the validator cannot be set as Annotations support is disabled.');
}
@@ -1281,7 +1281,7 @@ private function registerValidationConfiguration(array $config, ContainerBuilder
$validatorBuilder->addMethodCall('enableAnnotationMapping', [new Reference('annotation_reader')]);
}
- if (array_key_exists('static_method', $config) && $config['static_method']) {
+ if (\array_key_exists('static_method', $config) && $config['static_method']) {
foreach ($config['static_method'] as $methodName) {
$validatorBuilder->addMethodCall('addMethodMapping', [$methodName]);
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
index daf7a37bf8547..b472275ff530a 100644
--- a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
+++ b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
@@ -101,7 +101,7 @@ public function build(ContainerBuilder $container)
// but as late as possible to get resolved parameters
$container->addCompilerPass((new RegisterListenersPass())->setHotPathEvents($hotPathEvents), PassConfig::TYPE_BEFORE_REMOVING);
$container->addCompilerPass(new TemplatingPass());
- $this->addCompilerPassIfExists($container, AddConstraintValidatorsPass::class, PassConfig::TYPE_BEFORE_REMOVING);
+ $this->addCompilerPassIfExists($container, AddConstraintValidatorsPass::class);
$container->addCompilerPass(new AddAnnotationsCachedReaderPass(), PassConfig::TYPE_AFTER_REMOVING, -255);
$this->addCompilerPassIfExists($container, AddValidatorInitializersPass::class);
$this->addCompilerPassIfExists($container, AddConsoleCommandPass::class, PassConfig::TYPE_BEFORE_REMOVING);
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
index adbf4e5c574ef..531f7dddb970f 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
@@ -120,6 +120,7 @@
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelShutdownOnTearDownTrait.php b/src/Symfony/Bundle/FrameworkBundle/Test/KernelShutdownOnTearDownTrait.php
new file mode 100644
index 0000000000000..b01dbb0494340
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Test/KernelShutdownOnTearDownTrait.php
@@ -0,0 +1,47 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\FrameworkBundle\Test;
+
+use PHPUnit\Framework\TestCase;
+
+// Auto-adapt to PHPUnit 8 that added a `void` return-type to the tearDown method
+
+if (method_exists(\ReflectionMethod::class, 'hasReturnType') && (new \ReflectionMethod(TestCase::class, 'tearDown'))->hasReturnType()) {
+ eval('
+ namespace Symfony\Bundle\FrameworkBundle\Test;
+
+ /**
+ * @internal
+ */
+ trait KernelShutdownOnTearDownTrait
+ {
+ protected function tearDown(): void
+ {
+ static::ensureKernelShutdown();
+ }
+ }
+');
+} else {
+ /**
+ * @internal
+ */
+ trait KernelShutdownOnTearDownTrait
+ {
+ /**
+ * @return void
+ */
+ protected function tearDown()
+ {
+ static::ensureKernelShutdown();
+ }
+ }
+}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php
index 0feb15380e1c4..978f65863220c 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php
@@ -23,6 +23,8 @@
*/
abstract class KernelTestCase extends TestCase
{
+ use KernelShutdownOnTearDownTrait;
+
protected static $class;
/**
@@ -208,7 +210,7 @@ protected static function createKernel(array $options = [])
}
/**
- * Shuts the kernel down if it was used in the test.
+ * Shuts the kernel down if it was used in the test - called by the tearDown method by default.
*/
protected static function ensureKernelShutdown()
{
@@ -220,12 +222,4 @@ protected static function ensureKernelShutdown()
}
}
}
-
- /**
- * Clean up Kernel usage in this test.
- */
- protected function tearDown()
- {
- static::ensureKernelShutdown();
- }
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php
index f211f283869eb..c2cded7ab0966 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php
@@ -54,7 +54,7 @@ public function testCacheIsFreshAfterCacheClearedWithWarmup()
// Ensure that all *.meta files are fresh
$finder = new Finder();
$metaFiles = $finder->files()->in($this->kernel->getCacheDir())->name('*.php.meta');
- // simply check that cache is warmed up
+ // check that cache is warmed up
$this->assertNotEmpty($metaFiles);
$configCacheFactory = new ConfigCacheFactory(true);
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php
index 0400efc25e291..bfec01aefc876 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php
@@ -21,7 +21,7 @@ class ContainerDebugCommandTest extends WebTestCase
{
public function testDumpContainerIfNotExists()
{
- static::bootKernel(['test_case' => 'ContainerDebug', 'root_config' => 'config.yml']);
+ static::bootKernel(['test_case' => 'ContainerDebug', 'root_config' => 'config.yml', 'debug' => true]);
$application = new Application(static::$kernel);
$application->setAutoExit(false);
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/WebTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/WebTestCase.php
index 3f7bb4c7ee680..00eda6570906d 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/WebTestCase.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/WebTestCase.php
@@ -62,7 +62,7 @@ protected static function createKernel(array $options = [])
$options['test_case'],
isset($options['root_config']) ? $options['root_config'] : 'config.yml',
isset($options['environment']) ? $options['environment'] : strtolower(static::getVarDir().$options['test_case']),
- isset($options['debug']) ? $options['debug'] : true
+ isset($options['debug']) ? $options['debug'] : false
);
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php
index 7e8d8f0972151..03b2ed6961b7d 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php
@@ -27,6 +27,8 @@ class FormHelperDivLayoutTest extends AbstractDivLayoutTest
*/
protected $engine;
+ protected static $supportedFeatureSetVersion = 304;
+
protected function getExtensions()
{
// should be moved to the Form component once absolute file paths are supported
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php
index fc5e7d17f3786..bd088078c32d1 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php
@@ -27,6 +27,8 @@ class FormHelperTableLayoutTest extends AbstractTableLayoutTest
*/
protected $engine;
+ protected static $supportedFeatureSetVersion = 304;
+
public function testStartTagHasNoActionAttributeWhenActionIsEmpty()
{
$form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, [
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
index 8d4a46f9f330a..2db17aa130c39 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
@@ -479,7 +479,7 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a
foreach ($this->factories as $position) {
foreach ($position as $factory) {
$key = str_replace('-', '_', $factory->getKey());
- if (array_key_exists($key, $firewall)) {
+ if (\array_key_exists($key, $firewall)) {
$listenerKeys[] = $key;
}
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Templating/Helper/LogoutUrlHelper.php b/src/Symfony/Bundle/SecurityBundle/Templating/Helper/LogoutUrlHelper.php
index 3597b14a909c8..3051ed38dc165 100644
--- a/src/Symfony/Bundle/SecurityBundle/Templating/Helper/LogoutUrlHelper.php
+++ b/src/Symfony/Bundle/SecurityBundle/Templating/Helper/LogoutUrlHelper.php
@@ -11,7 +11,6 @@
namespace Symfony\Bundle\SecurityBundle\Templating\Helper;
-use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator;
use Symfony\Component\Templating\Helper\Helper;
@@ -38,7 +37,7 @@ public function __construct(LogoutUrlGenerator $generator)
*/
public function getLogoutPath($key)
{
- return $this->generator->getLogoutPath($key, UrlGeneratorInterface::ABSOLUTE_PATH);
+ return $this->generator->getLogoutPath($key);
}
/**
@@ -50,7 +49,7 @@ public function getLogoutPath($key)
*/
public function getLogoutUrl($key)
{
- return $this->generator->getLogoutUrl($key, UrlGeneratorInterface::ABSOLUTE_URL);
+ return $this->generator->getLogoutUrl($key);
}
/**
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/WebTestCase.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/WebTestCase.php
index f1d23b6054ad7..9bcbc0532481d 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/WebTestCase.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/WebTestCase.php
@@ -62,7 +62,7 @@ protected static function createKernel(array $options = [])
$options['test_case'],
isset($options['root_config']) ? $options['root_config'] : 'config.yml',
isset($options['environment']) ? $options['environment'] : strtolower(static::getVarDir().$options['test_case']),
- isset($options['debug']) ? $options['debug'] : true
+ isset($options['debug']) ? $options['debug'] : false
);
}
diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php
index a51a82bf5bfde..58e136a6381d0 100644
--- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php
+++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php
@@ -193,7 +193,7 @@ private function getBundleHierarchy(ContainerBuilder $container, array $config)
$bundleHierarchy = [];
foreach ($container->getParameter('kernel.bundles_metadata') as $name => $bundle) {
- if (!array_key_exists($name, $bundleHierarchy)) {
+ if (!\array_key_exists($name, $bundleHierarchy)) {
$bundleHierarchy[$name] = [
'paths' => [],
'parents' => [],
@@ -222,7 +222,7 @@ private function getBundleHierarchy(ContainerBuilder $container, array $config)
$bundleHierarchy[$name]['parents'][] = $bundle['parent'];
- if (!array_key_exists($bundle['parent'], $bundleHierarchy)) {
+ if (!\array_key_exists($bundle['parent'], $bundleHierarchy)) {
$bundleHierarchy[$bundle['parent']] = [
'paths' => [],
'parents' => [],
diff --git a/src/Symfony/Bundle/WebServerBundle/Resources/router.php b/src/Symfony/Bundle/WebServerBundle/Resources/router.php
index a366b381aae47..30d6b258a29de 100644
--- a/src/Symfony/Bundle/WebServerBundle/Resources/router.php
+++ b/src/Symfony/Bundle/WebServerBundle/Resources/router.php
@@ -17,8 +17,8 @@
* If you have custom directory layout, then you have to write your own router
* and pass it as a value to 'router' option of server:run command.
*
- * @author: Michał Pipa
- * @author: Albert Jessurum
+ * @author Michał Pipa
+ * @author Albert Jessurum
*/
// Workaround https://bugs.php.net/64566
diff --git a/src/Symfony/Component/BrowserKit/Client.php b/src/Symfony/Component/BrowserKit/Client.php
index 294b1fb84bbfb..98553353d8ce3 100644
--- a/src/Symfony/Component/BrowserKit/Client.php
+++ b/src/Symfony/Component/BrowserKit/Client.php
@@ -452,7 +452,7 @@ public function back()
{
do {
$request = $this->history->back();
- } while (array_key_exists(serialize($request), $this->redirects));
+ } while (\array_key_exists(serialize($request), $this->redirects));
return $this->requestFromRequest($request, false);
}
@@ -466,7 +466,7 @@ public function forward()
{
do {
$request = $this->history->forward();
- } while (array_key_exists(serialize($request), $this->redirects));
+ } while (\array_key_exists(serialize($request), $this->redirects));
return $this->requestFromRequest($request, false);
}
diff --git a/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php b/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php
index 482751f620c64..5758a28618e27 100644
--- a/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php
+++ b/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php
@@ -21,11 +21,11 @@ protected function setUp()
{
parent::setUp();
- if (!array_key_exists('testDeferredSaveWithoutCommit', $this->skippedTests) && \defined('HHVM_VERSION')) {
+ if (!\array_key_exists('testDeferredSaveWithoutCommit', $this->skippedTests) && \defined('HHVM_VERSION')) {
$this->skippedTests['testDeferredSaveWithoutCommit'] = 'Destructors are called late on HHVM.';
}
- if (!array_key_exists('testPrune', $this->skippedTests) && !$this->createCachePool() instanceof PruneableInterface) {
+ if (!\array_key_exists('testPrune', $this->skippedTests) && !$this->createCachePool() instanceof PruneableInterface) {
$this->skippedTests['testPrune'] = 'Not a pruneable cache pool.';
}
}
diff --git a/src/Symfony/Component/Cache/Tests/Simple/CacheTestCase.php b/src/Symfony/Component/Cache/Tests/Simple/CacheTestCase.php
index 27003bf913eb9..ff9944a3400b2 100644
--- a/src/Symfony/Component/Cache/Tests/Simple/CacheTestCase.php
+++ b/src/Symfony/Component/Cache/Tests/Simple/CacheTestCase.php
@@ -21,7 +21,7 @@ protected function setUp()
{
parent::setUp();
- if (!array_key_exists('testPrune', $this->skippedTests) && !$this->createSimpleCache() instanceof PruneableInterface) {
+ if (!\array_key_exists('testPrune', $this->skippedTests) && !$this->createSimpleCache() instanceof PruneableInterface) {
$this->skippedTests['testPrune'] = 'Not a pruneable cache pool.';
}
}
diff --git a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php
index 6c2190ab754ad..9b7a84ab59991 100644
--- a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php
+++ b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php
@@ -135,7 +135,7 @@ public static function createConnection($servers, array $options = [])
$client->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
$client->setOption(\Memcached::OPT_NO_BLOCK, true);
$client->setOption(\Memcached::OPT_TCP_NODELAY, true);
- if (!array_key_exists('LIBKETAMA_COMPATIBLE', $options) && !array_key_exists(\Memcached::OPT_LIBKETAMA_COMPATIBLE, $options)) {
+ if (!\array_key_exists('LIBKETAMA_COMPATIBLE', $options) && !\array_key_exists(\Memcached::OPT_LIBKETAMA_COMPATIBLE, $options)) {
$client->setOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE, true);
}
foreach ($options as $name => $value) {
diff --git a/src/Symfony/Component/Cache/Traits/RedisTrait.php b/src/Symfony/Component/Cache/Traits/RedisTrait.php
index a637be8080030..7cbd304a0cec7 100644
--- a/src/Symfony/Component/Cache/Traits/RedisTrait.php
+++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php
@@ -169,18 +169,29 @@ public static function createConnection($dsn, array $options = [])
*/
protected function doFetch(array $ids)
{
- if ($ids) {
+ if (!$ids) {
+ return [];
+ }
+
+ $result = [];
+
+ if ($this->redis instanceof \Predis\Client && $this->redis->getConnection() instanceof ClusterInterface) {
$values = $this->pipeline(function () use ($ids) {
foreach ($ids as $id) {
yield 'get' => [$id];
}
});
- foreach ($values as $id => $v) {
- if ($v) {
- yield $id => parent::unserialize($v);
- }
+ } else {
+ $values = array_combine($ids, $this->redis->mget($ids));
+ }
+
+ foreach ($values as $id => $v) {
+ if ($v) {
+ $result[$id] = parent::unserialize($v);
}
}
+
+ return $result;
}
/**
diff --git a/src/Symfony/Component/Config/Definition/ArrayNode.php b/src/Symfony/Component/Config/Definition/ArrayNode.php
index 70baa5a0ab2c0..ac310819d4199 100644
--- a/src/Symfony/Component/Config/Definition/ArrayNode.php
+++ b/src/Symfony/Component/Config/Definition/ArrayNode.php
@@ -59,7 +59,7 @@ protected function preNormalize($value)
$normalized = [];
foreach ($value as $k => $v) {
- if (false !== strpos($k, '-') && false === strpos($k, '_') && !array_key_exists($normalizedKey = str_replace('-', '_', $k), $value)) {
+ if (false !== strpos($k, '-') && false === strpos($k, '_') && !\array_key_exists($normalizedKey = str_replace('-', '_', $k), $value)) {
$normalized[$normalizedKey] = $v;
} else {
$normalized[$k] = $v;
@@ -223,7 +223,7 @@ protected function finalizeValue($value)
}
foreach ($this->children as $name => $child) {
- if (!array_key_exists($name, $value)) {
+ if (!\array_key_exists($name, $value)) {
if ($child->isRequired()) {
$ex = new InvalidConfigurationException(sprintf('The child node "%s" at path "%s" must be configured.', $name, $this->getPath()));
$ex->setPath($this->getPath());
@@ -359,7 +359,7 @@ protected function mergeValues($leftSide, $rightSide)
foreach ($rightSide as $k => $v) {
// no conflict
- if (!array_key_exists($k, $leftSide)) {
+ if (!\array_key_exists($k, $leftSide)) {
if (!$this->allowNewKeys) {
$ex = new InvalidConfigurationException(sprintf('You are not allowed to define new elements for path "%s". Please define all elements for this path in one config file. If you are trying to overwrite an element, make sure you redefine it with the same name.', $this->getPath()));
$ex->setPath($this->getPath());
diff --git a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php
index 9837e5d88321e..115054a24fb0c 100644
--- a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php
+++ b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php
@@ -332,10 +332,10 @@ public function performNoDeepMerging()
* Allows extra config keys to be specified under an array without
* throwing an exception.
*
- * Those config values are simply ignored and removed from the
- * resulting array. This should be used only in special cases where
- * you want to send an entire configuration array through a special
- * tree that processes only part of the array.
+ * Those config values are ignored and removed from the resulting
+ * array. This should be used only in special cases where you want
+ * to send an entire configuration array through a special tree that
+ * processes only part of the array.
*
* @param bool $remove Whether to remove the extra keys
*
diff --git a/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php b/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php
index f00cfcaa8349c..9a14449c487e3 100644
--- a/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php
+++ b/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php
@@ -245,7 +245,7 @@ protected function normalizeValue($value)
// if only "value" is left
if (array_keys($v) === ['value']) {
$v = $v['value'];
- if ($this->prototype instanceof ArrayNode && ($children = $this->prototype->getChildren()) && array_key_exists('value', $children)) {
+ if ($this->prototype instanceof ArrayNode && ($children = $this->prototype->getChildren()) && \array_key_exists('value', $children)) {
$valuePrototype = current($this->valuePrototypes) ?: clone $children['value'];
$valuePrototype->parent = $this;
$originalClosures = $this->prototype->normalizationClosures;
@@ -258,7 +258,7 @@ protected function normalizeValue($value)
}
}
- if (array_key_exists($k, $normalized)) {
+ if (\array_key_exists($k, $normalized)) {
$ex = new DuplicateKeyException(sprintf('Duplicate key "%s" for path "%s".', $k, $this->getPath()));
$ex->setPath($this->getPath());
@@ -301,14 +301,14 @@ protected function mergeValues($leftSide, $rightSide)
}
foreach ($rightSide as $k => $v) {
- // prototype, and key is irrelevant, so simply append the element
+ // prototype, and key is irrelevant, append the element
if (null === $this->keyAttribute) {
$leftSide[] = $v;
continue;
}
// no conflict
- if (!array_key_exists($k, $leftSide)) {
+ if (!\array_key_exists($k, $leftSide)) {
if (!$this->allowNewKeys) {
$ex = new InvalidConfigurationException(sprintf('You are not allowed to define new elements for path "%s". Please define all elements for this path in one config file.', $this->getPath()));
$ex->setPath($this->getPath());
diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php
index 0d52007454022..6cc5f5c580ecf 100644
--- a/src/Symfony/Component/Console/Application.php
+++ b/src/Symfony/Component/Console/Application.php
@@ -202,6 +202,13 @@ public function doRun(InputInterface $input, OutputInterface $output)
return 0;
}
+ try {
+ // Makes ArgvInput::getFirstArgument() able to distinguish an option from an argument.
+ $input->bind($this->getDefinition());
+ } catch (ExceptionInterface $e) {
+ // Errors must be ignored, full binding/validation happens later when the command is known.
+ }
+
$name = $this->getCommandName($input);
if (true === $input->hasParameterOption(['--help', '-h'], true)) {
if (!$name) {
diff --git a/src/Symfony/Component/Console/Helper/ProgressBar.php b/src/Symfony/Component/Console/Helper/ProgressBar.php
index a1c3cc5d217e7..172565514128b 100644
--- a/src/Symfony/Component/Console/Helper/ProgressBar.php
+++ b/src/Symfony/Component/Console/Helper/ProgressBar.php
@@ -466,19 +466,16 @@ private function overwrite($message)
{
if ($this->overwrite) {
if (!$this->firstRun) {
- // Move the cursor to the beginning of the line
- $this->output->write("\x0D");
-
- // Erase the line
- $this->output->write("\x1B[2K");
-
// Erase previous lines
if ($this->formatLineCount > 0) {
- $this->output->write(str_repeat("\x1B[1A\x1B[2K", $this->formatLineCount));
+ $message = str_repeat("\x1B[1A\x1B[2K", $this->formatLineCount).$message;
}
+
+ // Move the cursor to the beginning of the line and erase the line
+ $message = "\x0D\x1B[2K$message";
}
} elseif ($this->step > 0) {
- $this->output->writeln('');
+ $message = PHP_EOL.$message;
}
$this->firstRun = false;
diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php
index d3188897f37c8..9c200fe897ea9 100644
--- a/src/Symfony/Component/Console/Helper/QuestionHelper.php
+++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php
@@ -166,7 +166,7 @@ private function doAsk(OutputInterface $output, Question $question)
if (false === $ret) {
$ret = fgets($inputStream, 4096);
if (false === $ret) {
- throw new RuntimeException('Aborted');
+ throw new RuntimeException('Aborted.');
}
$ret = trim($ret);
}
@@ -252,8 +252,10 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
while (!feof($inputStream)) {
$c = fread($inputStream, 1);
- // Backspace Character
- if ("\177" === $c) {
+ // 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())) {
+ throw new RuntimeException('Aborted.');
+ } elseif ("\177" === $c) { // Backspace Character
if (0 === $numMatches && 0 !== $i) {
--$i;
// Move cursor backwards
@@ -306,6 +308,10 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
continue;
} else {
+ if ("\x80" <= $c) {
+ $c .= fread($inputStream, ["\xC0" => 1, "\xD0" => 1, "\xE0" => 2, "\xF0" => 3][$c & "\xF0"]);
+ }
+
$output->write($c);
$ret .= $c;
++$i;
@@ -380,7 +386,7 @@ private function getHiddenResponse(OutputInterface $output, $inputStream)
shell_exec(sprintf('stty %s', $sttyMode));
if (false === $value) {
- throw new RuntimeException('Aborted');
+ throw new RuntimeException('Aborted.');
}
$value = trim($value);
diff --git a/src/Symfony/Component/Console/Input/ArgvInput.php b/src/Symfony/Component/Console/Input/ArgvInput.php
index 142096ae62793..f604057ec0255 100644
--- a/src/Symfony/Component/Console/Input/ArgvInput.php
+++ b/src/Symfony/Component/Console/Input/ArgvInput.php
@@ -262,8 +262,27 @@ private function addLongOption($name, $value)
*/
public function getFirstArgument()
{
- foreach ($this->tokens as $token) {
+ $isOption = false;
+ foreach ($this->tokens as $i => $token) {
if ($token && '-' === $token[0]) {
+ if (false !== strpos($token, '=') || !isset($this->tokens[$i + 1])) {
+ continue;
+ }
+
+ // If it's a long option, consider that everything after "--" is the option name.
+ // Otherwise, use the last char (if it's a short option set, only the last one can take a value with space separator)
+ $name = '-' === $token[1] ? substr($token, 2) : substr($token, -1);
+ if (!isset($this->options[$name]) && !$this->definition->hasShortcut($name)) {
+ // noop
+ } elseif ((isset($this->options[$name]) || isset($this->options[$name = $this->definition->shortcutToName($name)])) && $this->tokens[$i + 1] === $this->options[$name]) {
+ $isOption = true;
+ }
+
+ continue;
+ }
+
+ if ($isOption) {
+ $isOption = false;
continue;
}
diff --git a/src/Symfony/Component/Console/Input/ArrayInput.php b/src/Symfony/Component/Console/Input/ArrayInput.php
index cf09ff45ecb49..44c2f0d5c66aa 100644
--- a/src/Symfony/Component/Console/Input/ArrayInput.php
+++ b/src/Symfony/Component/Console/Input/ArrayInput.php
@@ -19,7 +19,7 @@
*
* Usage:
*
- * $input = new ArrayInput(['name' => 'foo', '--bar' => 'foobar']);
+ * $input = new ArrayInput(['command' => 'foo:bar', 'foo' => 'bar', '--bar' => 'foobar']);
*
* @author Fabien Potencier
*/
diff --git a/src/Symfony/Component/Console/Input/Input.php b/src/Symfony/Component/Console/Input/Input.php
index 7a16e0ee4b9f2..c1220316dcfff 100644
--- a/src/Symfony/Component/Console/Input/Input.php
+++ b/src/Symfony/Component/Console/Input/Input.php
@@ -69,7 +69,7 @@ public function validate()
$givenArguments = $this->arguments;
$missingArguments = array_filter(array_keys($definition->getArguments()), function ($argument) use ($definition, $givenArguments) {
- return !array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired();
+ return !\array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired();
});
if (\count($missingArguments) > 0) {
@@ -150,7 +150,7 @@ public function getOption($name)
throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
}
- return array_key_exists($name, $this->options) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
+ return \array_key_exists($name, $this->options) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
}
/**
diff --git a/src/Symfony/Component/Console/Input/InputDefinition.php b/src/Symfony/Component/Console/Input/InputDefinition.php
index 9dc1c09b94e3a..53f41d73f5ce2 100644
--- a/src/Symfony/Component/Console/Input/InputDefinition.php
+++ b/src/Symfony/Component/Console/Input/InputDefinition.php
@@ -338,8 +338,10 @@ public function getOptionDefaults()
* @return string The InputOption name
*
* @throws InvalidArgumentException When option given does not exist
+ *
+ * @internal
*/
- private function shortcutToName($shortcut)
+ public function shortcutToName($shortcut)
{
if (!isset($this->shortcuts[$shortcut])) {
throw new InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut));
diff --git a/src/Symfony/Component/Console/Tester/ApplicationTester.php b/src/Symfony/Component/Console/Tester/ApplicationTester.php
index bcf91d640b7df..dc970182ecae6 100644
--- a/src/Symfony/Component/Console/Tester/ApplicationTester.php
+++ b/src/Symfony/Component/Console/Tester/ApplicationTester.php
@@ -66,7 +66,7 @@ public function run(array $input, $options = [])
$this->input->setInteractive($options['interactive']);
}
- $this->captureStreamsIndependently = array_key_exists('capture_stderr_separately', $options) && $options['capture_stderr_separately'];
+ $this->captureStreamsIndependently = \array_key_exists('capture_stderr_separately', $options) && $options['capture_stderr_separately'];
if (!$this->captureStreamsIndependently) {
$this->output = new StreamOutput(fopen('php://memory', 'w', false));
if (isset($options['decorated'])) {
diff --git a/src/Symfony/Component/Console/Tester/CommandTester.php b/src/Symfony/Component/Console/Tester/CommandTester.php
index 229471e13a35e..f869995eb92d6 100644
--- a/src/Symfony/Component/Console/Tester/CommandTester.php
+++ b/src/Symfony/Component/Console/Tester/CommandTester.php
@@ -62,9 +62,8 @@ public function execute(array $input, array $options = [])
}
$this->input = new ArrayInput($input);
- if ($this->inputs) {
- $this->input->setStream(self::createStream($this->inputs));
- }
+ // Use an in-memory input stream even if no inputs are set so that QuestionHelper::ask() does not rely on the blocking STDIN.
+ $this->input->setStream(self::createStream($this->inputs));
if (isset($options['interactive'])) {
$this->input->setInteractive($options['interactive']);
diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php
index 2cb3324630a08..ae9d130f502ab 100644
--- a/src/Symfony/Component/Console/Tests/ApplicationTest.php
+++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php
@@ -884,6 +884,19 @@ public function testRun()
$this->assertSame('called'.PHP_EOL, $tester->getDisplay(), '->run() does not call interact() if -n is passed');
}
+ public function testRunWithGlobalOptionAndNoCommand()
+ {
+ $application = new Application();
+ $application->setAutoExit(false);
+ $application->setCatchExceptions(false);
+ $application->getDefinition()->addOption(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL));
+
+ $output = new StreamOutput(fopen('php://memory', 'w', false));
+ $input = new ArgvInput(['cli.php', '--foo', 'bar']);
+
+ $this->assertSame(0, $application->run($input, $output));
+ }
+
/**
* Issue #9285.
*
diff --git a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php
index 531474eddfe1e..46cfe8184278d 100644
--- a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php
+++ b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php
@@ -237,6 +237,43 @@ public function testAskWithAutocompleteWithExactMatch()
$this->assertSame('b', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
}
+ public function getInputs()
+ {
+ return [
+ ['$'], // 1 byte character
+ ['¢'], // 2 bytes character
+ ['€'], // 3 bytes character
+ ['𐍈'], // 4 bytes character
+ ];
+ }
+
+ /**
+ * @dataProvider getInputs
+ */
+ public function testAskWithAutocompleteWithMultiByteCharacter($character)
+ {
+ if (!$this->hasSttyAvailable()) {
+ $this->markTestSkipped('`stty` is required to test autocomplete functionality');
+ }
+
+ $inputStream = $this->getInputStream("$character\n");
+
+ $possibleChoices = [
+ '$' => '1 byte character',
+ '¢' => '2 bytes character',
+ '€' => '3 bytes character',
+ '𐍈' => '4 bytes character',
+ ];
+
+ $dialog = new QuestionHelper();
+ $dialog->setHelperSet(new HelperSet([new FormatterHelper()]));
+
+ $question = new ChoiceQuestion('Please select a character', $possibleChoices);
+ $question->setMaxAttempts(1);
+
+ $this->assertSame($character, $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
+ }
+
public function testAutocompleteWithTrailingBackslash()
{
if (!$this->hasSttyAvailable()) {
@@ -900,7 +937,7 @@ public function testLegacyChoiceOutputFormattingQuestionForUtf8Keys()
/**
* @expectedException \Symfony\Component\Console\Exception\RuntimeException
- * @expectedExceptionMessage Aborted
+ * @expectedExceptionMessage Aborted.
*/
public function testAskThrowsExceptionOnMissingInput()
{
@@ -910,7 +947,17 @@ public function testAskThrowsExceptionOnMissingInput()
/**
* @expectedException \Symfony\Component\Console\Exception\RuntimeException
- * @expectedExceptionMessage Aborted
+ * @expectedExceptionMessage Aborted.
+ */
+ public function testAskThrowsExceptionOnMissingInputForChoiceQuestion()
+ {
+ $dialog = new QuestionHelper();
+ $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream('')), $this->createOutputInterface(), new ChoiceQuestion('Choice', ['a', 'b']));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Console\Exception\RuntimeException
+ * @expectedExceptionMessage Aborted.
*/
public function testAskThrowsExceptionOnMissingInputWithValidator()
{
diff --git a/src/Symfony/Component/Console/Tests/Helper/SymfonyQuestionHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/SymfonyQuestionHelperTest.php
index e7fd806064d2c..cf7a78c34ecda 100644
--- a/src/Symfony/Component/Console/Tests/Helper/SymfonyQuestionHelperTest.php
+++ b/src/Symfony/Component/Console/Tests/Helper/SymfonyQuestionHelperTest.php
@@ -124,7 +124,7 @@ public function testLabelTrailingBackslash()
/**
* @expectedException \Symfony\Component\Console\Exception\RuntimeException
- * @expectedExceptionMessage Aborted
+ * @expectedExceptionMessage Aborted.
*/
public function testAskThrowsExceptionOnMissingInput()
{
diff --git a/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php b/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php
index 37caaf2d1cd72..e20bcdd21bc7c 100644
--- a/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php
+++ b/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php
@@ -312,6 +312,14 @@ public function testGetFirstArgument()
$input = new ArgvInput(['cli.php', '-fbbar', 'foo']);
$this->assertEquals('foo', $input->getFirstArgument(), '->getFirstArgument() returns the first argument from the raw input');
+
+ $input = new ArgvInput(['cli.php', '--foo', 'fooval', 'bar']);
+ $input->bind(new InputDefinition([new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputArgument('arg')]));
+ $this->assertSame('bar', $input->getFirstArgument());
+
+ $input = new ArgvInput(['cli.php', '-bf', 'fooval', 'argval']);
+ $input->bind(new InputDefinition([new InputOption('bar', 'b', InputOption::VALUE_NONE), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputArgument('arg')]));
+ $this->assertSame('argval', $input->getFirstArgument());
}
public function testHasParameterOption()
diff --git a/src/Symfony/Component/Console/Tests/Tester/CommandTesterTest.php b/src/Symfony/Component/Console/Tests/Tester/CommandTesterTest.php
index 8cad7f0385d08..f916b1821fcc8 100644
--- a/src/Symfony/Component/Console/Tests/Tester/CommandTesterTest.php
+++ b/src/Symfony/Component/Console/Tests/Tester/CommandTesterTest.php
@@ -17,6 +17,7 @@
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Output\Output;
+use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Console\Tester\CommandTester;
@@ -139,7 +140,7 @@ public function testCommandWithDefaultInputs()
/**
* @expectedException \RuntimeException
- * @expectedMessage Aborted
+ * @expectedExceptionMessage Aborted.
*/
public function testCommandWithWrongInputsNumber()
{
@@ -153,13 +154,40 @@ public function testCommandWithWrongInputsNumber()
$command->setHelperSet(new HelperSet([new QuestionHelper()]));
$command->setCode(function ($input, $output) use ($questions, $command) {
$helper = $command->getHelper('question');
+ $helper->ask($input, $output, new ChoiceQuestion('choice', ['a', 'b']));
+ $helper->ask($input, $output, new Question($questions[0]));
+ $helper->ask($input, $output, new Question($questions[1]));
+ $helper->ask($input, $output, new Question($questions[2]));
+ });
+
+ $tester = new CommandTester($command);
+ $tester->setInputs(['a', 'Bobby', 'Fine']);
+ $tester->execute([]);
+ }
+
+ /**
+ * @expectedException \RuntimeException
+ * @expectedExceptionMessage Aborted.
+ */
+ public function testCommandWithQuestionsButNoInputs()
+ {
+ $questions = [
+ 'What\'s your name?',
+ 'How are you?',
+ 'Where do you come from?',
+ ];
+
+ $command = new Command('foo');
+ $command->setHelperSet(new HelperSet([new QuestionHelper()]));
+ $command->setCode(function ($input, $output) use ($questions, $command) {
+ $helper = $command->getHelper('question');
+ $helper->ask($input, $output, new ChoiceQuestion('choice', ['a', 'b']));
$helper->ask($input, $output, new Question($questions[0]));
$helper->ask($input, $output, new Question($questions[1]));
$helper->ask($input, $output, new Question($questions[2]));
});
$tester = new CommandTester($command);
- $tester->setInputs(['Bobby', 'Fine']);
$tester->execute([]);
}
diff --git a/src/Symfony/Component/Debug/DebugClassLoader.php b/src/Symfony/Component/Debug/DebugClassLoader.php
index 54df7de0cfc98..310f7d4e2247c 100644
--- a/src/Symfony/Component/Debug/DebugClassLoader.php
+++ b/src/Symfony/Component/Debug/DebugClassLoader.php
@@ -390,6 +390,11 @@ private function darwinRealpath($real)
$dirFiles = self::$darwinCache[$kDir][1];
+ if (!isset($dirFiles[$file]) && ') : eval()\'d code' === substr($file, -17)) {
+ // Get the file name from "file_name.php(123) : eval()'d code"
+ $file = substr($file, 0, strrpos($file, '(', -17));
+ }
+
if (isset($dirFiles[$file])) {
return $real .= $dirFiles[$file];
}
diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php
index 22a6426dab7e9..d871b91538c22 100644
--- a/src/Symfony/Component/Debug/ErrorHandler.php
+++ b/src/Symfony/Component/Debug/ErrorHandler.php
@@ -222,7 +222,7 @@ public function setLoggers(array $loggers)
}
if (!\is_array($log)) {
$log = [$log];
- } elseif (!array_key_exists(0, $log)) {
+ } elseif (!\array_key_exists(0, $log)) {
throw new \InvalidArgumentException('No logger provided');
}
if (null === $log[0]) {
@@ -517,6 +517,11 @@ public function handleError($type, $message, $file, $line)
$errorAsException ? ['exception' => $errorAsException] : [],
];
} else {
+ if (!\defined('HHVM_VERSION')) {
+ $currentErrorHandler = set_error_handler('var_dump');
+ restore_error_handler();
+ }
+
try {
$this->isRecursive = true;
$level = ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG;
@@ -525,7 +530,7 @@ public function handleError($type, $message, $file, $line)
$this->isRecursive = false;
if (!\defined('HHVM_VERSION')) {
- set_error_handler([$this, __FUNCTION__]);
+ set_error_handler($currentErrorHandler);
}
}
}
diff --git a/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php b/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php
index 73fee9ac15727..c6bc3f7d88b5e 100644
--- a/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php
+++ b/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php
@@ -297,7 +297,7 @@ public function testExtendedFinalClass()
require __DIR__.'/Fixtures/FinalClasses.php';
$i = 1;
- while(class_exists($finalClass = __NAMESPACE__.'\\Fixtures\\FinalClass'.$i++, false)) {
+ while (class_exists($finalClass = __NAMESPACE__.'\\Fixtures\\FinalClass'.$i++, false)) {
spl_autoload_call($finalClass);
class_exists('Test\\'.__NAMESPACE__.'\\Extends'.substr($finalClass, strrpos($finalClass, '\\') + 1), true);
}
@@ -385,6 +385,11 @@ class_exists('Test\\'.__NAMESPACE__.'\\UseTraitWithInternalMethod', true);
$this->assertSame([], $deprecations);
}
+
+ public function testEvaluatedCode()
+ {
+ $this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\DefinitionInEvaluatedCode', true));
+ }
}
class ClassLoader
diff --git a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php
index 6e0afd55cacf0..c1dea75bbd90e 100644
--- a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php
+++ b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php
@@ -12,12 +12,12 @@
namespace Symfony\Component\Debug\Tests;
use PHPUnit\Framework\TestCase;
-use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use Psr\Log\NullLogger;
use Symfony\Component\Debug\BufferingLogger;
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\Exception\SilencedErrorContext;
+use Symfony\Component\Debug\Tests\Fixtures\ErrorHandlerThatUsesThePreviousOne;
use Symfony\Component\Debug\Tests\Fixtures\LoggerThatSetAnErrorHandler;
/**
@@ -590,26 +590,40 @@ public function testCustomExceptionHandler()
}
/**
- * @dataProvider errorHandlerIsNotLostWhenLoggingProvider
+ * @dataProvider errorHandlerWhenLoggingProvider
*/
- public function testErrorHandlerIsNotLostWhenLogging($customErrorHandlerHasBeenPreviouslyDefined, LoggerInterface $logger)
+ public function testErrorHandlerWhenLogging($previousHandlerWasDefined, $loggerSetsAnotherHandler, $nextHandlerIsDefined)
{
try {
- if ($customErrorHandlerHasBeenPreviouslyDefined) {
+ if ($previousHandlerWasDefined) {
set_error_handler('count');
}
+ $logger = $loggerSetsAnotherHandler ? new LoggerThatSetAnErrorHandler() : new NullLogger();
+
$handler = ErrorHandler::register();
$handler->setDefaultLogger($logger);
+ if ($nextHandlerIsDefined) {
+ $handler = ErrorHandlerThatUsesThePreviousOne::register();
+ }
+
@trigger_error('foo', E_USER_DEPRECATED);
@trigger_error('bar', E_USER_DEPRECATED);
$this->assertSame([$handler, 'handleError'], set_error_handler('var_dump'));
+ if ($logger instanceof LoggerThatSetAnErrorHandler) {
+ $this->assertCount(2, $logger->cleanLogs());
+ }
+
restore_error_handler();
- if ($customErrorHandlerHasBeenPreviouslyDefined) {
+ if ($previousHandlerWasDefined) {
+ restore_error_handler();
+ }
+
+ if ($nextHandlerIsDefined) {
restore_error_handler();
}
} finally {
@@ -618,13 +632,14 @@ public function testErrorHandlerIsNotLostWhenLogging($customErrorHandlerHasBeenP
}
}
- public function errorHandlerIsNotLostWhenLoggingProvider()
+ public function errorHandlerWhenLoggingProvider()
{
- return [
- [false, new NullLogger()],
- [true, new NullLogger()],
- [false, new LoggerThatSetAnErrorHandler()],
- [true, new LoggerThatSetAnErrorHandler()],
- ];
+ foreach ([false, true] as $previousHandlerWasDefined) {
+ foreach ([false, true] as $loggerSetsAnotherHandler) {
+ foreach ([false, true] as $nextHandlerIsDefined) {
+ yield [$previousHandlerWasDefined, $loggerSetsAnotherHandler, $nextHandlerIsDefined];
+ }
+ }
+ }
}
}
diff --git a/src/Symfony/Component/Debug/Tests/Fixtures/DefinitionInEvaluatedCode.php b/src/Symfony/Component/Debug/Tests/Fixtures/DefinitionInEvaluatedCode.php
new file mode 100644
index 0000000000000..ff6976e0fb1d7
--- /dev/null
+++ b/src/Symfony/Component/Debug/Tests/Fixtures/DefinitionInEvaluatedCode.php
@@ -0,0 +1,11 @@
+arguments)) {
+ if (\array_key_exists('index_'.$index, $this->arguments)) {
return $this->arguments['index_'.$index];
}
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php
index b411b30f262b5..252b304f118f5 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php
@@ -214,7 +214,7 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
}
foreach ($parameters as $index => $parameter) {
- if (array_key_exists($index, $arguments) && '' !== $arguments[$index]) {
+ if (\array_key_exists($index, $arguments) && '' !== $arguments[$index]) {
continue;
}
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php b/src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php
index 8eac9f0061c5f..bf1387c04ed17 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php
@@ -43,10 +43,10 @@ protected function processValue($value, $isRoot = false)
if ([] !== array_diff(array_keys($attributes), ['id', 'key'])) {
throw new InvalidArgumentException(sprintf('The "container.service_subscriber" tag accepts only the "key" and "id" attributes, "%s" given for service "%s".', implode('", "', array_keys($attributes)), $this->currentId));
}
- if (!array_key_exists('id', $attributes)) {
+ if (!\array_key_exists('id', $attributes)) {
throw new InvalidArgumentException(sprintf('Missing "id" attribute on "container.service_subscriber" tag with key="%s" for service "%s".', $attributes['key'], $this->currentId));
}
- if (!array_key_exists('key', $attributes)) {
+ if (!\array_key_exists('key', $attributes)) {
$attributes['key'] = $attributes['id'];
}
if (isset($serviceMap[$attributes['key']])) {
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php
index 0fad285e5aaf4..20b262b6d461c 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php
@@ -119,11 +119,11 @@ protected function processValue($value, $isRoot = false)
}
foreach ($reflectionMethod->getParameters() as $key => $parameter) {
- if (array_key_exists($key, $arguments) && '' !== $arguments[$key]) {
+ if (\array_key_exists($key, $arguments) && '' !== $arguments[$key]) {
continue;
}
- if (array_key_exists('$'.$parameter->name, $bindings)) {
+ if (\array_key_exists('$'.$parameter->name, $bindings)) {
$arguments[$key] = $this->getBindingValue($bindings['$'.$parameter->name]);
continue;
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php
index ee54288ec3244..0b79280110f65 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php
@@ -71,7 +71,7 @@ protected function processValue($value, $isRoot = false)
$typeFound = false;
foreach ($parameters as $j => $p) {
- if (!array_key_exists($j, $resolvedArguments) && ProxyHelper::getTypeHint($r, $p, true) === $key) {
+ if (!\array_key_exists($j, $resolvedArguments) && ProxyHelper::getTypeHint($r, $p, true) === $key) {
$resolvedArguments[$j] = $argument;
$typeFound = true;
}
diff --git a/src/Symfony/Component/DependencyInjection/Container.php b/src/Symfony/Component/DependencyInjection/Container.php
index ea4a6d7631f1c..e822643439c2f 100644
--- a/src/Symfony/Component/DependencyInjection/Container.php
+++ b/src/Symfony/Component/DependencyInjection/Container.php
@@ -466,7 +466,7 @@ protected function getEnv($name)
if (isset($this->resolving[$envName = "env($name)"])) {
throw new ParameterCircularReferenceException(array_keys($this->resolving));
}
- if (isset($this->envCache[$name]) || array_key_exists($name, $this->envCache)) {
+ if (isset($this->envCache[$name]) || \array_key_exists($name, $this->envCache)) {
return $this->envCache[$name];
}
if (!$this->has($id = 'container.env_var_processors_locator')) {
diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php
index 171bcaf46a36f..ee58034713b0b 100644
--- a/src/Symfony/Component/DependencyInjection/Definition.php
+++ b/src/Symfony/Component/DependencyInjection/Definition.php
@@ -263,7 +263,7 @@ public function replaceArgument($index, $argument)
throw new OutOfBoundsException(sprintf('The index "%d" is not in the range [0, %d].', $index, \count($this->arguments) - 1));
}
- if (!array_key_exists($index, $this->arguments)) {
+ if (!\array_key_exists($index, $this->arguments)) {
throw new OutOfBoundsException(sprintf('The argument "%s" doesn\'t exist.', $index));
}
@@ -308,7 +308,7 @@ public function getArguments()
*/
public function getArgument($index)
{
- if (!array_key_exists($index, $this->arguments)) {
+ if (!\array_key_exists($index, $this->arguments)) {
throw new OutOfBoundsException(sprintf('The argument "%s" doesn\'t exist.', $index));
}
diff --git a/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php
index 3adcef79b195e..f06e6e80d5031 100644
--- a/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php
+++ b/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php
@@ -193,7 +193,7 @@ private function findNodes()
}
foreach ($container->getServiceIds() as $id) {
- if (array_key_exists($id, $container->getAliases())) {
+ if (\array_key_exists($id, $container->getAliases())) {
continue;
}
diff --git a/src/Symfony/Component/DependencyInjection/Extension/Extension.php b/src/Symfony/Component/DependencyInjection/Extension/Extension.php
index 80a9419c5d3f3..a9389862ccbe2 100644
--- a/src/Symfony/Component/DependencyInjection/Extension/Extension.php
+++ b/src/Symfony/Component/DependencyInjection/Extension/Extension.php
@@ -115,7 +115,7 @@ final public function getProcessedConfigs()
*/
protected function isConfigEnabled(ContainerBuilder $container, array $config)
{
- if (!array_key_exists('enabled', $config)) {
+ if (!\array_key_exists('enabled', $config)) {
throw new InvalidArgumentException("The config array has no 'enabled' key.");
}
diff --git a/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/RealServiceInstantiator.php b/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/RealServiceInstantiator.php
index 3b0b57ef0f6b9..532e768684f30 100644
--- a/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/RealServiceInstantiator.php
+++ b/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/RealServiceInstantiator.php
@@ -17,7 +17,7 @@
/**
* {@inheritdoc}
*
- * Noop proxy instantiator - simply produces the real service instead of a proxy instance.
+ * Noop proxy instantiator - produces the real service instead of a proxy instance.
*
* @author Marco Pivetta
*/
diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
index a02a6c8ea5c66..fd2a20a8768f7 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
@@ -333,7 +333,7 @@ private function parseDefinition(\DOMElement $service, $file, array $defaults)
continue;
}
- if (false !== strpos($name, '-') && false === strpos($name, '_') && !array_key_exists($normalizedName = str_replace('-', '_', $name), $parameters)) {
+ if (false !== strpos($name, '-') && false === strpos($name, '_') && !\array_key_exists($normalizedName = str_replace('-', '_', $name), $parameters)) {
$parameters[$normalizedName] = XmlUtils::phpize($node->nodeValue);
}
// keep not normalized key
diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
index 5dc41e0347895..a3a799024e499 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
@@ -213,7 +213,7 @@ private function parseDefinitions(array $content, $file)
throw new InvalidArgumentException(sprintf('The "services" key should contain an array in %s. Check your YAML syntax.', $file));
}
- if (array_key_exists('_instanceof', $content['services'])) {
+ if (\array_key_exists('_instanceof', $content['services'])) {
$instanceof = $content['services']['_instanceof'];
unset($content['services']['_instanceof']);
@@ -250,7 +250,7 @@ private function parseDefinitions(array $content, $file)
*/
private function parseDefaults(array &$content, $file)
{
- if (!array_key_exists('_defaults', $content['services'])) {
+ if (!\array_key_exists('_defaults', $content['services'])) {
return [];
}
$defaults = $content['services']['_defaults'];
@@ -361,7 +361,7 @@ private function parseDefinition($id, $service, $file, array $defaults)
if (isset($service['alias'])) {
$this->container->setAlias($id, $alias = new Alias($service['alias']));
- if (array_key_exists('public', $service)) {
+ if (\array_key_exists('public', $service)) {
$alias->setPublic($service['public']);
} elseif (isset($defaults['public'])) {
$alias->setPublic($defaults['public']);
@@ -438,7 +438,7 @@ private function parseDefinition($id, $service, $file, array $defaults)
$definition->setAbstract($service['abstract']);
}
- if (array_key_exists('deprecated', $service)) {
+ if (\array_key_exists('deprecated', $service)) {
$definition->setDeprecated(true, $service['deprecated']);
}
@@ -571,11 +571,11 @@ private function parseDefinition($id, $service, $file, array $defaults)
}
}
- if (array_key_exists('namespace', $service) && !array_key_exists('resource', $service)) {
+ if (\array_key_exists('namespace', $service) && !\array_key_exists('resource', $service)) {
throw new InvalidArgumentException(sprintf('A "resource" attribute must be set when the "namespace" attribute is set for service "%s" in %s. Check your YAML syntax.', $id, $file));
}
- if (array_key_exists('resource', $service)) {
+ if (\array_key_exists('resource', $service)) {
if (!\is_string($service['resource'])) {
throw new InvalidArgumentException(sprintf('A "resource" attribute must be of type string for service "%s" in %s. Check your YAML syntax.', $id, $file));
}
diff --git a/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php b/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php
index 8a86f94170a75..c4e702181fe68 100644
--- a/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php
+++ b/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php
@@ -70,7 +70,7 @@ public function get($name)
{
$name = $this->normalizeName($name);
- if (!array_key_exists($name, $this->parameters)) {
+ if (!\array_key_exists($name, $this->parameters)) {
if (!$name) {
throw new ParameterNotFoundException($name);
}
@@ -121,7 +121,7 @@ public function set($name, $value)
*/
public function has($name)
{
- return array_key_exists($this->normalizeName($name), $this->parameters);
+ return \array_key_exists($this->normalizeName($name), $this->parameters);
}
/**
diff --git a/src/Symfony/Component/DomCrawler/Crawler.php b/src/Symfony/Component/DomCrawler/Crawler.php
index b2355dedf9a65..68d7c3dafce88 100644
--- a/src/Symfony/Component/DomCrawler/Crawler.php
+++ b/src/Symfony/Component/DomCrawler/Crawler.php
@@ -968,7 +968,7 @@ private function relativize($xpath)
$expressions = [];
// An expression which will never match to replace expressions which cannot match in the crawler
- // We cannot simply drop
+ // We cannot drop
$nonMatchingExpression = 'a[name() = "b"]';
$xpathLen = \strlen($xpath);
diff --git a/src/Symfony/Component/DomCrawler/Field/FileFormField.php b/src/Symfony/Component/DomCrawler/Field/FileFormField.php
index af9c6e6f25ff4..9e21c9c4b9bd1 100644
--- a/src/Symfony/Component/DomCrawler/Field/FileFormField.php
+++ b/src/Symfony/Component/DomCrawler/Field/FileFormField.php
@@ -60,7 +60,7 @@ public function setValue($value)
// copy to a tmp location
$tmp = sys_get_temp_dir().'/'.strtr(substr(base64_encode(hash('sha256', uniqid(mt_rand(), true), true)), 0, 7), '/', '_');
- if (array_key_exists('extension', $info)) {
+ if (\array_key_exists('extension', $info)) {
$tmp .= '.'.$info['extension'];
}
if (is_file($tmp)) {
diff --git a/src/Symfony/Component/DomCrawler/FormFieldRegistry.php b/src/Symfony/Component/DomCrawler/FormFieldRegistry.php
index 10c7e8cb365b2..8f432cfbbb830 100644
--- a/src/Symfony/Component/DomCrawler/FormFieldRegistry.php
+++ b/src/Symfony/Component/DomCrawler/FormFieldRegistry.php
@@ -57,7 +57,7 @@ public function remove($name)
$target = &$this->fields;
while (\count($segments) > 1) {
$path = array_shift($segments);
- if (!array_key_exists($path, $target)) {
+ if (!\array_key_exists($path, $target)) {
return;
}
$target = &$target[$path];
@@ -80,7 +80,7 @@ public function &get($name)
$target = &$this->fields;
while ($segments) {
$path = array_shift($segments);
- if (!array_key_exists($path, $target)) {
+ if (!\array_key_exists($path, $target)) {
throw new \InvalidArgumentException(sprintf('Unreachable field "%s"', $path));
}
$target = &$target[$path];
diff --git a/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php b/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php
index c90ac927b2c84..de2b850953c3d 100644
--- a/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php
+++ b/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php
@@ -29,6 +29,7 @@ class WrappedListener
private $dispatcher;
private $pretty;
private $stub;
+ private $priority;
private static $hasClassStub;
public function __construct($listener, $name, Stopwatch $stopwatch, EventDispatcherInterface $dispatcher = null)
@@ -96,7 +97,7 @@ public function getInfo($eventName)
return [
'event' => $eventName,
- 'priority' => null !== $this->dispatcher ? $this->dispatcher->getListenerPriority($eventName, $this->listener) : null,
+ 'priority' => null !== $this->priority ? $this->priority : (null !== $this->dispatcher ? $this->dispatcher->getListenerPriority($eventName, $this->listener) : null),
'pretty' => $this->pretty,
'stub' => $this->stub,
];
@@ -104,11 +105,14 @@ public function getInfo($eventName)
public function __invoke(Event $event, $eventName, EventDispatcherInterface $dispatcher)
{
+ $dispatcher = $this->dispatcher ?: $dispatcher;
+
$this->called = true;
+ $this->priority = $dispatcher->getListenerPriority($eventName, $this->listener);
$e = $this->stopwatch->start($this->name, 'event_listener');
- \call_user_func($this->listener, $event, $eventName, $this->dispatcher ?: $dispatcher);
+ \call_user_func($this->listener, $event, $eventName, $dispatcher);
if ($e->isStarted()) {
$e->stop();
diff --git a/src/Symfony/Component/EventDispatcher/GenericEvent.php b/src/Symfony/Component/EventDispatcher/GenericEvent.php
index a9a0a5dfa59c6..f005e3a3db076 100644
--- a/src/Symfony/Component/EventDispatcher/GenericEvent.php
+++ b/src/Symfony/Component/EventDispatcher/GenericEvent.php
@@ -111,7 +111,7 @@ public function setArguments(array $args = [])
*/
public function hasArgument($key)
{
- return array_key_exists($key, $this->arguments);
+ return \array_key_exists($key, $this->arguments);
}
/**
diff --git a/src/Symfony/Component/Filesystem/Exception/IOExceptionInterface.php b/src/Symfony/Component/Filesystem/Exception/IOExceptionInterface.php
index c11965a4247ee..f9d4644a87279 100644
--- a/src/Symfony/Component/Filesystem/Exception/IOExceptionInterface.php
+++ b/src/Symfony/Component/Filesystem/Exception/IOExceptionInterface.php
@@ -21,7 +21,7 @@ interface IOExceptionInterface extends ExceptionInterface
/**
* Returns the associated path for the exception.
*
- * @return string The path
+ * @return string|null The path
*/
public function getPath();
}
diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php
index aa9afb349d30a..3c61fc15048fe 100644
--- a/src/Symfony/Component/Filesystem/Filesystem.php
+++ b/src/Symfony/Component/Filesystem/Filesystem.php
@@ -137,8 +137,8 @@ public function exists($files)
* Sets access and modification time of file.
*
* @param string|iterable $files A filename, an array of files, or a \Traversable instance to create
- * @param int $time The touch time as a Unix timestamp
- * @param int $atime The access time as a Unix timestamp
+ * @param int|null $time The touch time as a Unix timestamp, if not supplied the current system time is used
+ * @param int|null $atime The access time as a Unix timestamp, if not supplied the current system time is used
*
* @throws IOException When touch fails
*/
@@ -193,7 +193,7 @@ public function remove($files)
* @param int $umask The mode mask (octal)
* @param bool $recursive Whether change the mod recursively or not
*
- * @throws IOException When the change fail
+ * @throws IOException When the change fails
*/
public function chmod($files, $mode, $umask = 0000, $recursive = false)
{
@@ -214,7 +214,7 @@ public function chmod($files, $mode, $umask = 0000, $recursive = false)
* @param string $user The new owner user name
* @param bool $recursive Whether change the owner recursively or not
*
- * @throws IOException When the change fail
+ * @throws IOException When the change fails
*/
public function chown($files, $user, $recursive = false)
{
@@ -241,7 +241,7 @@ public function chown($files, $user, $recursive = false)
* @param string $group The group name
* @param bool $recursive Whether change the group recursively or not
*
- * @throws IOException When the change fail
+ * @throws IOException When the change fails
*/
public function chgrp($files, $group, $recursive = false)
{
@@ -519,14 +519,14 @@ public function makePathRelative($endPath, $startPath)
* - existing files in the target directory will be overwritten, except if they are newer (see the `override` option)
* - files in the target directory that do not exist in the source directory will not be deleted (see the `delete` option)
*
- * @param string $originDir The origin directory
- * @param string $targetDir The target directory
- * @param \Traversable $iterator Iterator that filters which files and directories to copy
- * @param array $options An array of boolean options
- * Valid options are:
- * - $options['override'] If true, target files newer than origin files are overwritten (see copy(), defaults to false)
- * - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink(), defaults to false)
- * - $options['delete'] Whether to delete files that are not in the source directory (defaults to false)
+ * @param string $originDir The origin directory
+ * @param string $targetDir The target directory
+ * @param \Traversable|null $iterator Iterator that filters which files and directories to copy, if null a recursive iterator is created
+ * @param array $options An array of boolean options
+ * Valid options are:
+ * - $options['override'] If true, target files newer than origin files are overwritten (see copy(), defaults to false)
+ * - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink(), defaults to false)
+ * - $options['delete'] Whether to delete files that are not in the source directory (defaults to false)
*
* @throws IOException When file type is unknown
*/
diff --git a/src/Symfony/Component/Finder/Finder.php b/src/Symfony/Component/Finder/Finder.php
index f03882bba1de7..133c4b8c50b15 100644
--- a/src/Symfony/Component/Finder/Finder.php
+++ b/src/Symfony/Component/Finder/Finder.php
@@ -29,7 +29,7 @@
*
* All rules may be invoked several times.
*
- * All methods return the current Finder object to allow easy chaining:
+ * All methods return the current Finder object to allow chaining:
*
* $finder = Finder::create()->files()->name('*.php')->in(__DIR__);
*
@@ -645,12 +645,15 @@ public function count()
*/
private function searchInDirectory($dir)
{
+ $exclude = $this->exclude;
+ $notPaths = $this->notPaths;
+
if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) {
- $this->exclude = array_merge($this->exclude, self::$vcsPatterns);
+ $exclude = array_merge($exclude, self::$vcsPatterns);
}
if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) {
- $this->notPaths[] = '#(^|/)\..+(/|$)#';
+ $notPaths[] = '#(^|/)\..+(/|$)#';
}
$minDepth = 0;
@@ -683,8 +686,8 @@ private function searchInDirectory($dir)
$iterator = new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs);
- if ($this->exclude) {
- $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude);
+ if ($exclude) {
+ $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $exclude);
}
$iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST);
@@ -717,8 +720,8 @@ private function searchInDirectory($dir)
$iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
}
- if ($this->paths || $this->notPaths) {
- $iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $this->notPaths);
+ if ($this->paths || $notPaths) {
+ $iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $notPaths);
}
if ($this->sort) {
diff --git a/src/Symfony/Component/Finder/Tests/FinderTest.php b/src/Symfony/Component/Finder/Tests/FinderTest.php
index d309d23b1767a..aac35bc234195 100644
--- a/src/Symfony/Component/Finder/Tests/FinderTest.php
+++ b/src/Symfony/Component/Finder/Tests/FinderTest.php
@@ -199,6 +199,18 @@ public function testIgnoreVCS()
$this->assertIterator($this->toAbsolute(['foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo bar']), $finder->in(self::$tmpDir)->getIterator());
}
+ public function testIgnoreVCSCanBeDisabledAfterFirstIteration()
+ {
+ $finder = $this->buildFinder();
+ $finder->in(self::$tmpDir);
+ $finder->ignoreDotFiles(false);
+
+ $this->assertIterator($this->toAbsolute(['foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo bar']), $finder->getIterator());
+
+ $finder->ignoreVCS(false);
+ $this->assertIterator($this->toAbsolute(['.git', 'foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'toto/.git', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo bar']), $finder->getIterator());
+ }
+
public function testIgnoreDotFiles()
{
$finder = $this->buildFinder();
@@ -214,6 +226,17 @@ public function testIgnoreDotFiles()
$this->assertIterator($this->toAbsolute(['foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'foo bar']), $finder->in(self::$tmpDir)->getIterator());
}
+ public function testIgnoreDotFilesCanBeDisabledAfterFirstIteration()
+ {
+ $finder = $this->buildFinder();
+ $finder->in(self::$tmpDir);
+
+ $this->assertIterator($this->toAbsolute(['foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'foo bar']), $finder->getIterator());
+
+ $finder->ignoreDotFiles(false);
+ $this->assertIterator($this->toAbsolute(['foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo bar']), $finder->getIterator());
+ }
+
public function testSortByName()
{
$finder = $this->buildFinder();
diff --git a/src/Symfony/Component/Form/ButtonBuilder.php b/src/Symfony/Component/Form/ButtonBuilder.php
index d2044f214ed52..903e842b9e8b8 100644
--- a/src/Symfony/Component/Form/ButtonBuilder.php
+++ b/src/Symfony/Component/Form/ButtonBuilder.php
@@ -664,7 +664,7 @@ public function getAttributes()
*/
public function hasAttribute($name)
{
- return array_key_exists($name, $this->attributes);
+ return \array_key_exists($name, $this->attributes);
}
/**
@@ -677,7 +677,7 @@ public function hasAttribute($name)
*/
public function getAttribute($name, $default = null)
{
- return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
+ return \array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
}
/**
@@ -771,7 +771,7 @@ public function getOptions()
*/
public function hasOption($name)
{
- return array_key_exists($name, $this->options);
+ return \array_key_exists($name, $this->options);
}
/**
@@ -784,7 +784,7 @@ public function hasOption($name)
*/
public function getOption($name, $default = null)
{
- return array_key_exists($name, $this->options) ? $this->options[$name] : $default;
+ return \array_key_exists($name, $this->options) ? $this->options[$name] : $default;
}
/**
diff --git a/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php b/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php
index c33dda70ca6df..02685a2504185 100644
--- a/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php
+++ b/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php
@@ -78,7 +78,7 @@ public function __construct($choices, callable $value = null)
// If a deterministic value generator was passed, use it later
$this->valueCallback = $value;
} else {
- // Otherwise simply generate incrementing integers as values
+ // Otherwise generate incrementing integers as values
$i = 0;
$value = function () use (&$i) {
return $i++;
@@ -135,7 +135,7 @@ public function getChoicesForValues(array $values)
$choices = [];
foreach ($values as $i => $givenValue) {
- if (array_key_exists($givenValue, $this->choices)) {
+ if (\array_key_exists($givenValue, $this->choices)) {
$choices[$i] = $this->choices[$givenValue];
}
}
diff --git a/src/Symfony/Component/Form/ChoiceList/LazyChoiceList.php b/src/Symfony/Component/Form/ChoiceList/LazyChoiceList.php
index b81ad48b91896..0fb32734d5318 100644
--- a/src/Symfony/Component/Form/ChoiceList/LazyChoiceList.php
+++ b/src/Symfony/Component/Form/ChoiceList/LazyChoiceList.php
@@ -32,7 +32,7 @@ class LazyChoiceList implements ChoiceListInterface
/**
* The callable creating string values for each choice.
*
- * If null, choices are simply cast to strings.
+ * If null, choices are cast to strings.
*
* @var callable|null
*/
diff --git a/src/Symfony/Component/Form/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Component/Form/Console/Descriptor/JsonDescriptor.php
index d602a15f77766..ab518dbfeed12 100644
--- a/src/Symfony/Component/Form/Console/Descriptor/JsonDescriptor.php
+++ b/src/Symfony/Component/Form/Console/Descriptor/JsonDescriptor.php
@@ -66,7 +66,7 @@ protected function describeOption(OptionsResolver $optionsResolver, array $optio
'allowed_values' => 'allowedValues',
];
foreach ($map as $label => $name) {
- if (array_key_exists($name, $definition)) {
+ if (\array_key_exists($name, $definition)) {
$data[$label] = $definition[$name];
if ('default' === $name) {
diff --git a/src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php b/src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php
index 015caf13ad351..957944f9ebea9 100644
--- a/src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php
+++ b/src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php
@@ -110,7 +110,7 @@ protected function describeOption(OptionsResolver $optionsResolver, array $optio
];
$rows = [];
foreach ($map as $label => $name) {
- $value = array_key_exists($name, $definition) ? $dump($definition[$name]) : '-';
+ $value = \array_key_exists($name, $definition) ? $dump($definition[$name]) : '-';
if ('default' === $name && isset($definition['lazy'])) {
$value = "Value: $value\n\nClosure(s): ".$dump($definition['lazy']);
}
diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php
index 999e47391b576..ca69b5fe8a8b7 100644
--- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php
+++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php
@@ -11,6 +11,8 @@
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
+use Symfony\Component\Form\Exception\TransformationFailedException;
+
/**
* Transforms between an integer and a localized number with grouping
* (each thousand) and comma separators.
@@ -40,8 +42,22 @@ public function __construct($scale = 0, $grouping = false, $roundingMode = self:
*/
public function reverseTransform($value)
{
+ $decimalSeparator = $this->getNumberFormatter()->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
+
+ if (\is_string($value) && false !== strpos($value, $decimalSeparator)) {
+ throw new TransformationFailedException(sprintf('The value "%s" is not a valid integer.', $value));
+ }
+
$result = parent::reverseTransform($value);
return null !== $result ? (int) $result : null;
}
+
+ /**
+ * @internal
+ */
+ protected function castParsedValue($value)
+ {
+ return $value;
+ }
}
diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php
index 7449fedfc69cc..b163cc093bfac 100644
--- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php
+++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php
@@ -23,6 +23,12 @@ class MoneyToLocalizedStringTransformer extends NumberToLocalizedStringTransform
{
private $divisor;
+ /**
+ * @param int|null $scale
+ * @param bool|null $grouping
+ * @param int|null $roundingMode
+ * @param int|null $divisor
+ */
public function __construct($scale = 2, $grouping = true, $roundingMode = self::ROUND_HALF_UP, $divisor = 1)
{
if (null === $grouping) {
@@ -58,7 +64,7 @@ public function transform($value)
if (!is_numeric($value)) {
throw new TransformationFailedException('Expected a numeric.');
}
- $value = (string) ($value / $this->divisor);
+ $value /= $this->divisor;
}
return parent::transform($value);
diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php
index 638885e4226e6..d720bb8e77f45 100644
--- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php
+++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php
@@ -78,6 +78,11 @@ class NumberToLocalizedStringTransformer implements DataTransformerInterface
private $scale;
+ /**
+ * @param int|null $scale
+ * @param bool|null $grouping
+ * @param int|null $roundingMode
+ */
public function __construct($scale = null, $grouping = false, $roundingMode = self::ROUND_HALF_UP)
{
if (null === $grouping) {
@@ -181,9 +186,7 @@ public function reverseTransform($value)
throw new TransformationFailedException('I don\'t have a clear idea what infinity looks like');
}
- if (\is_int($result) && $result === (int) $float = (float) $result) {
- $result = $float;
- }
+ $result = $this->castParsedValue($result);
if (false !== $encoding = mb_detect_encoding($value, null, true)) {
$length = mb_strlen($value, $encoding);
@@ -228,6 +231,18 @@ protected function getNumberFormatter()
return $formatter;
}
+ /**
+ * @internal
+ */
+ protected function castParsedValue($value)
+ {
+ if (\is_int($value) && $value === (int) $float = (float) $value) {
+ return $float;
+ }
+
+ return $value;
+ }
+
/**
* Rounds a number according to the configured scale and rounding mode.
*
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php
index 35429b2cb728c..72b14035e575c 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php
@@ -38,7 +38,7 @@ public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
- $isDataOptionSet = array_key_exists('data', $options);
+ $isDataOptionSet = \array_key_exists('data', $options);
$builder
->setRequired($options['required'])
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/IntegerType.php b/src/Symfony/Component/Form/Extension/Core/Type/IntegerType.php
index 3e119ed5ce26c..e3699614b4f69 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/IntegerType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/IntegerType.php
@@ -14,6 +14,8 @@
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\DataTransformer\IntegerToLocalizedStringTransformer;
use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\Form\FormInterface;
+use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;
class IntegerType extends AbstractType
@@ -31,6 +33,16 @@ public function buildForm(FormBuilderInterface $builder, array $options)
));
}
+ /**
+ * {@inheritdoc}
+ */
+ public function buildView(FormView $view, FormInterface $form, array $options)
+ {
+ if ($options['grouping']) {
+ $view->vars['type'] = 'text';
+ }
+ }
+
/**
* {@inheritdoc}
*/
diff --git a/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php b/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php
index 452bca443f5e9..8f90d21c297bd 100644
--- a/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php
+++ b/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php
@@ -59,7 +59,7 @@ public function preSubmit(FormEvent $event)
if ($form->isRoot() && $form->getConfig()->getOption('compound') && !$postRequestSizeExceeded) {
$data = $event->getData();
- if (!isset($data[$this->fieldName]) || !$this->tokenManager->isTokenValid(new CsrfToken($this->tokenId, $data[$this->fieldName]))) {
+ if (!isset($data[$this->fieldName]) || !\is_string($data[$this->fieldName]) || !$this->tokenManager->isTokenValid(new CsrfToken($this->tokenId, $data[$this->fieldName]))) {
$errorMessage = $this->errorMessage;
if (null !== $this->translator) {
diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php
index e7b485d39f8f7..88fa453a62580 100644
--- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php
+++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php
@@ -228,9 +228,6 @@ public function getData()
return $this->data;
}
- /**
- * @internal
- */
public function serialize()
{
if ($this->hasVarDumper) {
diff --git a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
index 08d679a4b5d4c..7922bcc74c87f 100644
--- a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
+++ b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
@@ -26,10 +26,10 @@ class FormValidator extends ConstraintValidator
/**
* {@inheritdoc}
*/
- public function validate($form, Constraint $constraint)
+ public function validate($form, Constraint $formConstraint)
{
- if (!$constraint instanceof Form) {
- throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Form');
+ if (!$formConstraint instanceof Form) {
+ throw new UnexpectedTypeException($formConstraint, __NAMESPACE__.'\Form');
}
if (!$form instanceof FormInterface) {
@@ -41,7 +41,7 @@ public function validate($form, Constraint $constraint)
$validator = $this->context->getValidator()->inContext($this->context);
- if ($form->isSynchronized()) {
+ if ($form->isSubmitted() && $form->isSynchronized()) {
// Validate the form data only if transformation succeeded
$groups = self::getValidationGroups($form);
$data = $form->getData();
@@ -62,8 +62,8 @@ public function validate($form, Constraint $constraint)
// Otherwise validate a constraint only once for the first
// matching group
foreach ($groups as $group) {
- if (\in_array($group, $constraint->groups)) {
- $validator->atPath('data')->validate($form->getData(), $constraint, $group);
+ if (\in_array($group, $formConstraint->groups)) {
+ $validator->atPath('data')->validate($form->getData(), $formConstraint, $group);
if (\count($this->context->getViolations()) > 0) {
break;
}
@@ -90,7 +90,7 @@ public function validate($form, Constraint $constraint)
}
}
}
- } else {
+ } elseif (!$form->isSynchronized()) {
$childrenSynchronized = true;
/** @var FormInterface $child */
@@ -113,7 +113,7 @@ public function validate($form, Constraint $constraint)
? (string) $form->getViewData()
: \gettype($form->getViewData());
- $this->context->setConstraint($constraint);
+ $this->context->setConstraint($formConstraint);
$this->context->buildViolation($config->getOption('invalid_message'))
->setParameters(array_replace(['{{ value }}' => $clientDataAsString], $config->getOption('invalid_message_parameters')))
->setInvalidValue($form->getViewData())
@@ -125,7 +125,7 @@ public function validate($form, Constraint $constraint)
// Mark the form with an error if it contains extra fields
if (!$config->getOption('allow_extra_fields') && \count($form->getExtraData()) > 0) {
- $this->context->setConstraint($constraint);
+ $this->context->setConstraint($formConstraint);
$this->context->buildViolation($config->getOption('extra_fields_message'))
->setParameter('{{ extra_fields }}', '"'.implode('", "', array_keys($form->getExtraData())).'"')
->setInvalidValue($form->getExtraData())
diff --git a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php
index 590f7df2d8b58..cb9f3f953326b 100644
--- a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php
+++ b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php
@@ -55,7 +55,7 @@ public function getOrigin()
*/
public function match($propertyPath)
{
- if ($propertyPath === (string) $this->propertyPath) {
+ if ($propertyPath === $this->propertyPath) {
return $this->getTarget();
}
}
diff --git a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php
index 86a77044d13d5..659c266ce6fa9 100644
--- a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php
+++ b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php
@@ -273,9 +273,6 @@ private function reconstructPath(ViolationPath $violationPath, FormInterface $or
*/
private function acceptsErrors(FormInterface $form)
{
- // Ignore non-submitted forms. This happens, for example, in PATCH
- // requests.
- // https://github.com/symfony/symfony/pull/10567
- return $form->isSubmitted() && ($this->allowNonSynchronized || $form->isSynchronized());
+ return $this->allowNonSynchronized || $form->isSynchronized();
}
}
diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php
index 1822cf6e18acc..cfa7eb5b6a6d8 100644
--- a/src/Symfony/Component/Form/Form.php
+++ b/src/Symfony/Component/Form/Form.php
@@ -42,9 +42,9 @@
*
* In most cases, format (1) and format (2) will be the same. For example,
* a checkbox field uses a Boolean value for both internal processing and
- * storage in the object. In these cases you simply need to set a view
- * transformer to convert between formats (2) and (3). You can do this by
- * calling addViewTransformer().
+ * storage in the object. In these cases you need to set a view transformer
+ * to convert between formats (2) and (3). You can do this by calling
+ * addViewTransformer().
*
* In some cases though it makes sense to make format (1) configurable. To
* demonstrate this, let's extend our above date field to store the value
@@ -574,7 +574,7 @@ public function submit($submittedData, $clearMissing = true)
}
foreach ($this->children as $name => $child) {
- $isSubmitted = array_key_exists($name, $submittedData);
+ $isSubmitted = \array_key_exists($name, $submittedData);
if ($isSubmitted || $clearMissing) {
$child->submit($isSubmitted ? $submittedData[$name] : null, $clearMissing);
diff --git a/src/Symfony/Component/Form/FormConfigBuilder.php b/src/Symfony/Component/Form/FormConfigBuilder.php
index 28094d27c9ea1..fc864fc617173 100644
--- a/src/Symfony/Component/Form/FormConfigBuilder.php
+++ b/src/Symfony/Component/Form/FormConfigBuilder.php
@@ -431,7 +431,7 @@ public function getAttributes()
*/
public function hasAttribute($name)
{
- return array_key_exists($name, $this->attributes);
+ return \array_key_exists($name, $this->attributes);
}
/**
@@ -439,7 +439,7 @@ public function hasAttribute($name)
*/
public function getAttribute($name, $default = null)
{
- return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
+ return \array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
}
/**
@@ -526,7 +526,7 @@ public function getOptions()
*/
public function hasOption($name)
{
- return array_key_exists($name, $this->options);
+ return \array_key_exists($name, $this->options);
}
/**
@@ -534,7 +534,7 @@ public function hasOption($name)
*/
public function getOption($name, $default = null)
{
- return array_key_exists($name, $this->options) ? $this->options[$name] : $default;
+ return \array_key_exists($name, $this->options) ? $this->options[$name] : $default;
}
/**
diff --git a/src/Symfony/Component/Form/FormFactory.php b/src/Symfony/Component/Form/FormFactory.php
index d474d5903f86f..b397f9a21fbfa 100644
--- a/src/Symfony/Component/Form/FormFactory.php
+++ b/src/Symfony/Component/Form/FormFactory.php
@@ -63,7 +63,7 @@ public function createBuilder($type = 'Symfony\Component\Form\Extension\Core\Typ
*/
public function createNamedBuilder($name, $type = 'Symfony\Component\Form\Extension\Core\Type\FormType', $data = null, array $options = [])
{
- if (null !== $data && !array_key_exists('data', $options)) {
+ if (null !== $data && !\array_key_exists('data', $options)) {
$options['data'] = $data;
}
diff --git a/src/Symfony/Component/Form/FormRenderer.php b/src/Symfony/Component/Form/FormRenderer.php
index 16f0a65ac1141..8b15e0344d0ce 100644
--- a/src/Symfony/Component/Form/FormRenderer.php
+++ b/src/Symfony/Component/Form/FormRenderer.php
@@ -163,7 +163,7 @@ public function searchAndRenderBlock(FormView $view, $blockNameSuffix, array $va
// "form_widget" in this example (again, no matter in which theme).
// If the designer wants to explicitly fallback to "form_widget" in their
// custom "choice_widget", for example because they only want to wrap
- // a
around the original implementation, they can simply call the
+ // a
around the original implementation, they can call the
// widget() function again to render the block for the parent type.
//
// The second kind is implemented in the following blocks.
diff --git a/src/Symfony/Component/Form/FormTypeGuesserInterface.php b/src/Symfony/Component/Form/FormTypeGuesserInterface.php
index 0aec6364c4ac7..6521ea47ca767 100644
--- a/src/Symfony/Component/Form/FormTypeGuesserInterface.php
+++ b/src/Symfony/Component/Form/FormTypeGuesserInterface.php
@@ -32,7 +32,7 @@ public function guessType($class, $property);
* @param string $class The fully qualified class name
* @param string $property The name of the property to guess for
*
- * @return Guess\ValueGuess A guess for the field's required setting
+ * @return Guess\ValueGuess|null A guess for the field's required setting
*/
public function guessRequired($class, $property);
diff --git a/src/Symfony/Component/Form/NativeRequestHandler.php b/src/Symfony/Component/Form/NativeRequestHandler.php
index ffe91ba7604dc..246ea92ccee50 100644
--- a/src/Symfony/Component/Form/NativeRequestHandler.php
+++ b/src/Symfony/Component/Form/NativeRequestHandler.php
@@ -94,10 +94,10 @@ public function handleRequest(FormInterface $form, $request = null)
if ('' === $name) {
$params = $_POST;
$files = $fixedFiles;
- } elseif (array_key_exists($name, $_POST) || array_key_exists($name, $fixedFiles)) {
+ } elseif (\array_key_exists($name, $_POST) || \array_key_exists($name, $fixedFiles)) {
$default = $form->getConfig()->getCompound() ? [] : null;
- $params = array_key_exists($name, $_POST) ? $_POST[$name] : $default;
- $files = array_key_exists($name, $fixedFiles) ? $fixedFiles[$name] : $default;
+ $params = \array_key_exists($name, $_POST) ? $_POST[$name] : $default;
+ $files = \array_key_exists($name, $fixedFiles) ? $fixedFiles[$name] : $default;
} else {
// Don't submit the form if it is not present in the request
return;
@@ -115,6 +115,10 @@ public function handleRequest(FormInterface $form, $request = null)
return;
}
+ if (\is_array($data) && \array_key_exists('_method', $data) && $method === $data['_method'] && !$form->has('_method')) {
+ unset($data['_method']);
+ }
+
$form->submit($data, 'PATCH' !== $method);
}
diff --git a/src/Symfony/Component/Form/Test/FormPerformanceTestCase.php b/src/Symfony/Component/Form/Test/FormPerformanceTestCase.php
index 01440575a1126..e11ce03357f6d 100644
--- a/src/Symfony/Component/Form/Test/FormPerformanceTestCase.php
+++ b/src/Symfony/Component/Form/Test/FormPerformanceTestCase.php
@@ -11,6 +11,8 @@
namespace Symfony\Component\Form\Test;
+use Symfony\Component\Form\Tests\VersionAwareTest;
+
/**
* Base class for performance tests.
*
@@ -21,6 +23,8 @@
*/
abstract class FormPerformanceTestCase extends FormIntegrationTestCase
{
+ use VersionAwareTest;
+
/**
* @var int
*/
diff --git a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php
index 0e749b780c784..4e01253e2e564 100644
--- a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php
+++ b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php
@@ -18,6 +18,8 @@
abstract class AbstractLayoutTest extends FormIntegrationTestCase
{
+ use VersionAwareTest;
+
protected $csrfTokenManager;
protected $testableFeatures = [];
@@ -1730,6 +1732,21 @@ public function testInteger()
);
}
+ public function testIntegerTypeWithGroupingRendersAsTextInput()
+ {
+ $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\IntegerType', 123, [
+ 'grouping' => true,
+ ]);
+
+ $this->assertWidgetMatchesXpath($form->createView(), [],
+'/input
+ [@type="text"]
+ [@name="name"]
+ [@value="123"]
+'
+ );
+ }
+
public function testLanguage()
{
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\LanguageType', 'de');
diff --git a/src/Symfony/Component/Form/Tests/AbstractRequestHandlerTest.php b/src/Symfony/Component/Form/Tests/AbstractRequestHandlerTest.php
index dd73dcde23e4e..16d4045e6d580 100644
--- a/src/Symfony/Component/Form/Tests/AbstractRequestHandlerTest.php
+++ b/src/Symfony/Component/Form/Tests/AbstractRequestHandlerTest.php
@@ -200,7 +200,7 @@ public function testMergeParamsAndFiles($method)
$form = $this->createForm('param1', $method, true);
$form->add($this->createForm('field1'));
$form->add($this->createBuilder('field2', false, ['allow_file_upload' => true])->getForm());
- $file = $this->getMockFile();
+ $file = $this->getUploadedFile();
$this->setRequestData($method, [
'param1' => [
@@ -225,7 +225,7 @@ public function testMergeParamsAndFiles($method)
public function testParamTakesPrecedenceOverFile($method)
{
$form = $this->createForm('param1', $method);
- $file = $this->getMockFile();
+ $file = $this->getUploadedFile();
$this->setRequestData($method, [
'param1' => 'DATA',
@@ -247,7 +247,7 @@ public function testSubmitFileIfNoParam($method)
$form = $this->createBuilder('param1', false, ['allow_file_upload' => true])
->setMethod($method)
->getForm();
- $file = $this->getMockFile();
+ $file = $this->getUploadedFile();
$this->setRequestData($method, [
'param1' => null,
@@ -269,14 +269,14 @@ public function testSubmitMultipleFiles($method)
$form = $this->createBuilder('param1', false, ['allow_file_upload' => true])
->setMethod($method)
->getForm();
- $file = $this->getMockFile();
+ $file = $this->getUploadedFile();
$this->setRequestData($method, [
'param1' => null,
], [
- 'param2' => $this->getMockFile('2'),
+ 'param2' => $this->getUploadedFile('2'),
'param1' => $file,
- 'param3' => $this->getMockFile('3'),
+ 'param3' => $this->getUploadedFile('3'),
]);
$this->requestHandler->handleRequest($form, $this->request);
@@ -285,6 +285,26 @@ public function testSubmitMultipleFiles($method)
$this->assertSame($file, $form->getData());
}
+ /**
+ * @dataProvider methodExceptGetProvider
+ */
+ public function testSubmitFileWithNamelessForm($method)
+ {
+ $form = $this->createForm('', $method, true);
+ $fileForm = $this->createBuilder('document', false, ['allow_file_upload' => true])->getForm();
+ $form->add($fileForm);
+ $file = $this->getUploadedFile();
+ $this->setRequestData($method, [
+ 'document' => null,
+ ], [
+ 'document' => $file,
+ ]);
+ $this->requestHandler->handleRequest($form, $this->request);
+
+ $this->assertTrue($form->isSubmitted());
+ $this->assertSame($file, $fileForm->getData());
+ }
+
/**
* @dataProvider getPostMaxSizeFixtures
*/
@@ -332,7 +352,7 @@ public function getPostMaxSizeFixtures()
public function testUploadedFilesAreAccepted()
{
- $this->assertTrue($this->requestHandler->isFileUpload($this->getMockFile()));
+ $this->assertTrue($this->requestHandler->isFileUpload($this->getUploadedFile()));
}
public function testInvalidFilesAreRejected()
@@ -344,7 +364,7 @@ abstract protected function setRequestData($method, $data, $files = []);
abstract protected function getRequestHandler();
- abstract protected function getMockFile($suffix = '');
+ abstract protected function getUploadedFile($suffix = '');
abstract protected function getInvalidFile();
diff --git a/src/Symfony/Component/Form/Tests/DependencyInjection/FormPassTest.php b/src/Symfony/Component/Form/Tests/DependencyInjection/FormPassTest.php
index 52fc78aed9087..a23607412cc8d 100644
--- a/src/Symfony/Component/Form/Tests/DependencyInjection/FormPassTest.php
+++ b/src/Symfony/Component/Form/Tests/DependencyInjection/FormPassTest.php
@@ -19,8 +19,9 @@
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\Command\DebugCommand;
use Symfony\Component\Form\DependencyInjection\FormPass;
-use Symfony\Component\Form\FormRegistryInterface;
+use Symfony\Component\Form\FormRegistry;
/**
* @author Bernhard Schussek
@@ -70,8 +71,12 @@ public function testAddTaggedTypesToDebugCommand()
{
$container = $this->createContainerBuilder();
+ $container->register('form.registry', FormRegistry::class);
+ $commandDefinition = new Definition(DebugCommand::class, [new Reference('form.registry')]);
+ $commandDefinition->setPublic(true);
+
$container->setDefinition('form.extension', $this->createExtensionDefinition());
- $container->setDefinition('console.command.form_debug', $this->createDebugCommandDefinition());
+ $container->setDefinition('console.command.form_debug', $commandDefinition);
$container->register('my.type1', __CLASS__.'_Type1')->addTag('form.type')->setPublic(true);
$container->register('my.type2', __CLASS__.'_Type2')->addTag('form.type')->setPublic(true);
@@ -259,19 +264,6 @@ private function createExtensionDefinition()
return $definition;
}
- private function createDebugCommandDefinition()
- {
- $definition = new Definition('Symfony\Component\Form\Command\DebugCommand');
- $definition->setPublic(true);
- $definition->setArguments([
- $formRegistry = $this->getMockBuilder(FormRegistryInterface::class)->getMock(),
- [],
- ['Symfony\Component\Form\Extension\Core\Type'],
- ]);
-
- return $definition;
- }
-
private function createContainerBuilder()
{
$container = new ContainerBuilder();
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php
index 901af61ce94fb..da351295c381e 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php
@@ -12,9 +12,14 @@
namespace Symfony\Component\Form\Tests\Extension\Core\DataMapper;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper;
+use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormConfigBuilder;
-use Symfony\Component\Form\FormConfigInterface;
+use Symfony\Component\PropertyAccess\PropertyAccess;
+use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
+use Symfony\Component\PropertyAccess\PropertyPath;
class PropertyPathMapperTest extends TestCase
{
@@ -24,78 +29,36 @@ class PropertyPathMapperTest extends TestCase
private $mapper;
/**
- * @var \PHPUnit_Framework_MockObject_MockObject
+ * @var EventDispatcherInterface
*/
private $dispatcher;
/**
- * @var \PHPUnit_Framework_MockObject_MockObject
+ * @var PropertyAccessorInterface
*/
private $propertyAccessor;
protected function setUp()
{
- $this->dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock();
- $this->propertyAccessor = $this->getMockBuilder('Symfony\Component\PropertyAccess\PropertyAccessorInterface')->getMock();
+ $this->dispatcher = new EventDispatcher();
+ $this->propertyAccessor = PropertyAccess::createPropertyAccessor();
$this->mapper = new PropertyPathMapper($this->propertyAccessor);
}
- /**
- * @return \PHPUnit_Framework_MockObject_MockObject
- */
- private function getPropertyPath($path)
- {
- return $this->getMockBuilder('Symfony\Component\PropertyAccess\PropertyPath')
- ->setConstructorArgs([$path])
- ->setMethods(['getValue', 'setValue'])
- ->getMock();
- }
-
- /**
- * @param FormConfigInterface $config
- * @param bool $synchronized
- * @param bool $submitted
- *
- * @return \PHPUnit_Framework_MockObject_MockObject
- */
- private function getForm(FormConfigInterface $config, $synchronized = true, $submitted = true)
- {
- $form = $this->getMockBuilder('Symfony\Component\Form\Form')
- ->setConstructorArgs([$config])
- ->setMethods(['isSynchronized', 'isSubmitted'])
- ->getMock();
-
- $form->expects($this->any())
- ->method('isSynchronized')
- ->will($this->returnValue($synchronized));
-
- $form->expects($this->any())
- ->method('isSubmitted')
- ->will($this->returnValue($submitted));
-
- return $form;
- }
-
public function testMapDataToFormsPassesObjectRefIfByReference()
{
$car = new \stdClass();
$engine = new \stdClass();
- $propertyPath = $this->getPropertyPath('engine');
-
- $this->propertyAccessor->expects($this->once())
- ->method('getValue')
- ->with($car, $propertyPath)
- ->will($this->returnValue($engine));
+ $car->engine = $engine;
+ $propertyPath = new PropertyPath('engine');
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
- $form = $this->getForm($config);
+ $form = new Form($config);
$this->mapper->mapDataToForms($car, [$form]);
- // Can't use isIdentical() above because mocks always clone their
- // arguments which can't be disabled in PHPUnit 3.6
$this->assertSame($engine, $form->getData());
}
@@ -103,17 +66,14 @@ public function testMapDataToFormsPassesObjectCloneIfNotByReference()
{
$car = new \stdClass();
$engine = new \stdClass();
- $propertyPath = $this->getPropertyPath('engine');
-
- $this->propertyAccessor->expects($this->once())
- ->method('getValue')
- ->with($car, $propertyPath)
- ->will($this->returnValue($engine));
+ $engine->brand = 'Rolls-Royce';
+ $car->engine = $engine;
+ $propertyPath = new PropertyPath('engine');
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(false);
$config->setPropertyPath($propertyPath);
- $form = $this->getForm($config);
+ $form = new Form($config);
$this->mapper->mapDataToForms($car, [$form]);
@@ -127,7 +87,7 @@ public function testMapDataToFormsIgnoresEmptyPropertyPath()
$config = new FormConfigBuilder(null, '\stdClass', $this->dispatcher);
$config->setByReference(true);
- $form = $this->getForm($config);
+ $form = new Form($config);
$this->assertNull($form->getPropertyPath());
@@ -139,16 +99,14 @@ public function testMapDataToFormsIgnoresEmptyPropertyPath()
public function testMapDataToFormsIgnoresUnmapped()
{
$car = new \stdClass();
- $propertyPath = $this->getPropertyPath('engine');
-
- $this->propertyAccessor->expects($this->never())
- ->method('getValue');
+ $car->engine = new \stdClass();
+ $propertyPath = new PropertyPath('engine');
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(true);
$config->setMapped(false);
$config->setPropertyPath($propertyPath);
- $form = $this->getForm($config);
+ $form = new Form($config);
$this->mapper->mapDataToForms($car, [$form]);
@@ -158,204 +116,188 @@ public function testMapDataToFormsIgnoresUnmapped()
public function testMapDataToFormsSetsDefaultDataIfPassedDataIsNull()
{
$default = new \stdClass();
- $propertyPath = $this->getPropertyPath('engine');
-
- $this->propertyAccessor->expects($this->never())
- ->method('getValue');
+ $propertyPath = new PropertyPath('engine');
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$config->setData($default);
- $form = $this->getMockBuilder('Symfony\Component\Form\Form')
- ->setConstructorArgs([$config])
- ->setMethods(['setData'])
- ->getMock();
-
- $form->expects($this->once())
- ->method('setData')
- ->with($default);
+ $form = new Form($config);
$this->mapper->mapDataToForms(null, [$form]);
+
+ $this->assertSame($default, $form->getData());
}
public function testMapDataToFormsSetsDefaultDataIfPassedDataIsEmptyArray()
{
$default = new \stdClass();
- $propertyPath = $this->getPropertyPath('engine');
-
- $this->propertyAccessor->expects($this->never())
- ->method('getValue');
+ $propertyPath = new PropertyPath('engine');
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$config->setData($default);
- $form = $this->getMockBuilder('Symfony\Component\Form\Form')
- ->setConstructorArgs([$config])
- ->setMethods(['setData'])
- ->getMock();
-
- $form->expects($this->once())
- ->method('setData')
- ->with($default);
+ $form = new Form($config);
$this->mapper->mapDataToForms([], [$form]);
+
+ $this->assertSame($default, $form->getData());
}
public function testMapFormsToDataWritesBackIfNotByReference()
{
$car = new \stdClass();
+ $car->engine = new \stdClass();
$engine = new \stdClass();
- $propertyPath = $this->getPropertyPath('engine');
-
- $this->propertyAccessor->expects($this->once())
- ->method('setValue')
- ->with($car, $propertyPath, $engine);
+ $engine->brand = 'Rolls-Royce';
+ $propertyPath = new PropertyPath('engine');
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(false);
$config->setPropertyPath($propertyPath);
$config->setData($engine);
- $form = $this->getForm($config);
+ $form = new SubmittedForm($config);
$this->mapper->mapFormsToData([$form], $car);
+
+ $this->assertEquals($engine, $car->engine);
+ $this->assertNotSame($engine, $car->engine);
}
public function testMapFormsToDataWritesBackIfByReferenceButNoReference()
{
$car = new \stdClass();
+ $car->engine = new \stdClass();
$engine = new \stdClass();
- $propertyPath = $this->getPropertyPath('engine');
-
- $this->propertyAccessor->expects($this->once())
- ->method('setValue')
- ->with($car, $propertyPath, $engine);
+ $propertyPath = new PropertyPath('engine');
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$config->setData($engine);
- $form = $this->getForm($config);
+ $form = new SubmittedForm($config);
$this->mapper->mapFormsToData([$form], $car);
+
+ $this->assertSame($engine, $car->engine);
}
public function testMapFormsToDataWritesBackIfByReferenceAndReference()
{
$car = new \stdClass();
- $engine = new \stdClass();
- $propertyPath = $this->getPropertyPath('engine');
-
- // $car already contains the reference of $engine
- $this->propertyAccessor->expects($this->once())
- ->method('getValue')
- ->with($car, $propertyPath)
- ->will($this->returnValue($engine));
+ $car->engine = 'BMW';
+ $propertyPath = new PropertyPath('engine');
- $this->propertyAccessor->expects($this->never())
- ->method('setValue');
-
- $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
+ $config = new FormConfigBuilder('engine', null, $this->dispatcher);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
- $config->setData($engine);
- $form = $this->getForm($config);
+ $config->setData('Rolls-Royce');
+ $form = new SubmittedForm($config);
+
+ $car->engine = 'Rolls-Royce';
$this->mapper->mapFormsToData([$form], $car);
+
+ $this->assertSame('Rolls-Royce', $car->engine);
}
public function testMapFormsToDataIgnoresUnmapped()
{
+ $initialEngine = new \stdClass();
$car = new \stdClass();
+ $car->engine = $initialEngine;
$engine = new \stdClass();
- $propertyPath = $this->getPropertyPath('engine');
-
- $this->propertyAccessor->expects($this->never())
- ->method('setValue');
+ $propertyPath = new PropertyPath('engine');
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$config->setData($engine);
$config->setMapped(false);
- $form = $this->getForm($config);
+ $form = new SubmittedForm($config);
$this->mapper->mapFormsToData([$form], $car);
+
+ $this->assertSame($initialEngine, $car->engine);
}
public function testMapFormsToDataIgnoresUnsubmittedForms()
{
+ $initialEngine = new \stdClass();
$car = new \stdClass();
+ $car->engine = $initialEngine;
$engine = new \stdClass();
- $propertyPath = $this->getPropertyPath('engine');
-
- $this->propertyAccessor->expects($this->never())
- ->method('setValue');
+ $propertyPath = new PropertyPath('engine');
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$config->setData($engine);
- $form = $this->getForm($config, true, false);
+ $form = new Form($config);
$this->mapper->mapFormsToData([$form], $car);
+
+ $this->assertSame($initialEngine, $car->engine);
}
public function testMapFormsToDataIgnoresEmptyData()
{
+ $initialEngine = new \stdClass();
$car = new \stdClass();
- $propertyPath = $this->getPropertyPath('engine');
-
- $this->propertyAccessor->expects($this->never())
- ->method('setValue');
+ $car->engine = $initialEngine;
+ $propertyPath = new PropertyPath('engine');
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$config->setData(null);
- $form = $this->getForm($config);
+ $form = new Form($config);
$this->mapper->mapFormsToData([$form], $car);
+
+ $this->assertSame($initialEngine, $car->engine);
}
public function testMapFormsToDataIgnoresUnsynchronized()
{
+ $initialEngine = new \stdClass();
$car = new \stdClass();
+ $car->engine = $initialEngine;
$engine = new \stdClass();
- $propertyPath = $this->getPropertyPath('engine');
-
- $this->propertyAccessor->expects($this->never())
- ->method('setValue');
+ $propertyPath = new PropertyPath('engine');
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$config->setData($engine);
- $form = $this->getForm($config, false);
+ $form = new NotSynchronizedForm($config);
$this->mapper->mapFormsToData([$form], $car);
+
+ $this->assertSame($initialEngine, $car->engine);
}
public function testMapFormsToDataIgnoresDisabled()
{
+ $initialEngine = new \stdClass();
$car = new \stdClass();
+ $car->engine = $initialEngine;
$engine = new \stdClass();
- $propertyPath = $this->getPropertyPath('engine');
-
- $this->propertyAccessor->expects($this->never())
- ->method('setValue');
+ $propertyPath = new PropertyPath('engine');
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$config->setData($engine);
$config->setDisabled(true);
- $form = $this->getForm($config);
+ $form = new Form($config);
$this->mapper->mapFormsToData([$form], $car);
+
+ $this->assertSame($initialEngine, $car->engine);
}
/**
@@ -365,24 +307,19 @@ public function testMapFormsToDataDoesNotChangeEqualDateTimeInstance($date)
{
$article = [];
$publishedAt = $date;
- $article['publishedAt'] = clone $publishedAt;
- $propertyPath = $this->getPropertyPath('[publishedAt]');
-
- $this->propertyAccessor->expects($this->once())
- ->method('getValue')
- ->willReturn($article['publishedAt'])
- ;
- $this->propertyAccessor->expects($this->never())
- ->method('setValue')
- ;
+ $publishedAtValue = clone $publishedAt;
+ $article['publishedAt'] = $publishedAtValue;
+ $propertyPath = new PropertyPath('[publishedAt]');
$config = new FormConfigBuilder('publishedAt', \get_class($publishedAt), $this->dispatcher);
$config->setByReference(false);
$config->setPropertyPath($propertyPath);
$config->setData($publishedAt);
- $form = $this->getForm($config);
+ $form = new SubmittedForm($config);
$this->mapper->mapFormsToData([$form], $article);
+
+ $this->assertSame($publishedAtValue, $article['publishedAt']);
}
public function provideDate()
@@ -393,3 +330,19 @@ public function provideDate()
];
}
}
+
+class SubmittedForm extends Form
+{
+ public function isSubmitted()
+ {
+ return true;
+ }
+}
+
+class NotSynchronizedForm extends Form
+{
+ public function isSynchronized()
+ {
+ return false;
+ }
+}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php
index 193aba91ab3b8..8cdbb1959a79e 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php
@@ -95,9 +95,7 @@ public function testReverseTransform()
$transformer = new IntegerToLocalizedStringTransformer();
$this->assertEquals(1, $transformer->reverseTransform('1'));
- $this->assertEquals(1, $transformer->reverseTransform('1,5'));
- $this->assertEquals(1234, $transformer->reverseTransform('1234,5'));
- $this->assertEquals(12345, $transformer->reverseTransform('12345,912'));
+ $this->assertEquals(12345, $transformer->reverseTransform('12345'));
}
public function testReverseTransformEmpty()
@@ -116,10 +114,10 @@ public function testReverseTransformWithGrouping()
$transformer = new IntegerToLocalizedStringTransformer(null, true);
- $this->assertEquals(1234, $transformer->reverseTransform('1.234,5'));
- $this->assertEquals(12345, $transformer->reverseTransform('12.345,912'));
- $this->assertEquals(1234, $transformer->reverseTransform('1234,5'));
- $this->assertEquals(12345, $transformer->reverseTransform('12345,912'));
+ $this->assertEquals(1234, $transformer->reverseTransform('1.234'));
+ $this->assertEquals(12345, $transformer->reverseTransform('12.345'));
+ $this->assertEquals(1234, $transformer->reverseTransform('1234'));
+ $this->assertEquals(12345, $transformer->reverseTransform('12345'));
}
public function reverseTransformWithRoundingProvider()
@@ -203,6 +201,29 @@ public function testReverseTransformExpectsValidNumber()
$transformer->reverseTransform('foo');
}
+ /**
+ * @dataProvider floatNumberProvider
+ * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
+ */
+ public function testReverseTransformExpectsInteger($number, $locale)
+ {
+ IntlTestHelper::requireFullIntl($this, false);
+
+ \Locale::setDefault($locale);
+
+ $transformer = new IntegerToLocalizedStringTransformer();
+
+ $transformer->reverseTransform($number);
+ }
+
+ public function floatNumberProvider()
+ {
+ return [
+ ['12345.912', 'en'],
+ ['1.234,5', 'de_DE'],
+ ];
+ }
+
/**
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
*/
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php
index d9fafdff13a35..c5bffe4d96e65 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php
@@ -17,6 +17,18 @@
class MoneyToLocalizedStringTransformerTest extends TestCase
{
+ private $previousLocale;
+
+ protected function setUp()
+ {
+ $this->previousLocale = setlocale(LC_ALL, '0');
+ }
+
+ protected function tearDown()
+ {
+ setlocale(LC_ALL, $this->previousLocale);
+ }
+
public function testTransform()
{
// Since we test against "de_AT", we need the full implementation
@@ -73,7 +85,7 @@ public function testReverseTransformEmpty()
$this->assertNull($transformer->reverseTransform(''));
}
- public function testFloatToIntConversionMismatchOnReversTransform()
+ public function testFloatToIntConversionMismatchOnReverseTransform()
{
$transformer = new MoneyToLocalizedStringTransformer(null, null, null, 100);
IntlTestHelper::requireFullIntl($this, false);
@@ -90,4 +102,16 @@ public function testFloatToIntConversionMismatchOnTransform()
$this->assertSame('10,20', $transformer->transform(1020));
}
+
+ public function testValidNumericValuesWithNonDotDecimalPointCharacter()
+ {
+ // calling setlocale() here is important as it changes the representation of floats when being cast to strings
+ setlocale(LC_ALL, 'de_AT.UTF-8');
+
+ $transformer = new MoneyToLocalizedStringTransformer(4, null, null, 100);
+ IntlTestHelper::requireFullIntl($this, false);
+ \Locale::setDefault('de_AT');
+
+ $this->assertSame('0,0035', $transformer->transform(12 / 34));
+ }
}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixUrlProtocolListenerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixUrlProtocolListenerTest.php
index 0182aa23d301e..e00cb9e9e1978 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixUrlProtocolListenerTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixUrlProtocolListenerTest.php
@@ -12,9 +12,10 @@
namespace Symfony\Component\Form\Tests\Extension\Core\EventListener;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Form\Extension\Core\EventListener\FixUrlProtocolListener;
use Symfony\Component\Form\Form;
-use Symfony\Component\Form\FormConfigInterface;
+use Symfony\Component\Form\FormConfigBuilder;
use Symfony\Component\Form\FormEvent;
class FixUrlProtocolListenerTest extends TestCase
@@ -22,7 +23,7 @@ class FixUrlProtocolListenerTest extends TestCase
public function testFixHttpUrl()
{
$data = 'www.symfony.com';
- $form = new Form($this->getMockBuilder(FormConfigInterface::class)->getMock());
+ $form = new Form(new FormConfigBuilder('name', null, new EventDispatcher()));
$event = new FormEvent($form, $data);
$filter = new FixUrlProtocolListener('http');
@@ -34,7 +35,7 @@ public function testFixHttpUrl()
public function testSkipKnownUrl()
{
$data = 'http://www.symfony.com';
- $form = new Form($this->getMockBuilder(FormConfigInterface::class)->getMock());
+ $form = new Form(new FormConfigBuilder('name', null, new EventDispatcher()));
$event = new FormEvent($form, $data);
$filter = new FixUrlProtocolListener('http');
@@ -59,7 +60,7 @@ public function provideUrlsWithSupportedProtocols()
*/
public function testSkipOtherProtocol($url)
{
- $form = new Form($this->getMockBuilder(FormConfigInterface::class)->getMock());
+ $form = new Form(new FormConfigBuilder('name', null, new EventDispatcher()));
$event = new FormEvent($form, $url);
$filter = new FixUrlProtocolListener('http');
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayObjectTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayObjectTest.php
index 6f46c9d7fab37..5944537927de9 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayObjectTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayObjectTest.php
@@ -11,7 +11,9 @@
namespace Symfony\Component\Form\Tests\Extension\Core\EventListener;
+use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Form\FormBuilder;
+use Symfony\Component\Form\FormFactoryBuilder;
class MergeCollectionListenerArrayObjectTest extends MergeCollectionListenerTest
{
@@ -22,6 +24,6 @@ protected function getData(array $data)
protected function getBuilder($name = 'name')
{
- return new FormBuilder($name, '\ArrayObject', $this->dispatcher, $this->factory);
+ return new FormBuilder($name, '\ArrayObject', new EventDispatcher(), (new FormFactoryBuilder())->getFormFactory());
}
}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayTest.php
index c0f3d59734bcb..4f19a3ff8e777 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayTest.php
@@ -11,7 +11,9 @@
namespace Symfony\Component\Form\Tests\Extension\Core\EventListener;
+use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Form\FormBuilder;
+use Symfony\Component\Form\FormFactoryBuilder;
class MergeCollectionListenerArrayTest extends MergeCollectionListenerTest
{
@@ -22,6 +24,6 @@ protected function getData(array $data)
protected function getBuilder($name = 'name')
{
- return new FormBuilder($name, null, $this->dispatcher, $this->factory);
+ return new FormBuilder($name, null, new EventDispatcher(), (new FormFactoryBuilder())->getFormFactory());
}
}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerCustomArrayObjectTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerCustomArrayObjectTest.php
index 76bdc7682366b..4be3b4babae98 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerCustomArrayObjectTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerCustomArrayObjectTest.php
@@ -11,7 +11,9 @@
namespace Symfony\Component\Form\Tests\Extension\Core\EventListener;
+use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Form\FormBuilder;
+use Symfony\Component\Form\FormFactoryBuilder;
use Symfony\Component\Form\Tests\Fixtures\CustomArrayObject;
class MergeCollectionListenerCustomArrayObjectTest extends MergeCollectionListenerTest
@@ -23,6 +25,6 @@ protected function getData(array $data)
protected function getBuilder($name = 'name')
{
- return new FormBuilder($name, 'Symfony\Component\Form\Tests\Fixtures\CustomArrayObject', $this->dispatcher, $this->factory);
+ return new FormBuilder($name, 'Symfony\Component\Form\Tests\Fixtures\CustomArrayObject', new EventDispatcher(), (new FormFactoryBuilder())->getFormFactory());
}
}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerTest.php
index 98cd78385a80d..8c691dffc1d75 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerTest.php
@@ -17,21 +17,15 @@
abstract class MergeCollectionListenerTest extends TestCase
{
- protected $dispatcher;
- protected $factory;
protected $form;
protected function setUp()
{
- $this->dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock();
- $this->factory = $this->getMockBuilder('Symfony\Component\Form\FormFactoryInterface')->getMock();
$this->form = $this->getForm('axes');
}
protected function tearDown()
{
- $this->dispatcher = null;
- $this->factory = null;
$this->form = null;
}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php
index 84a8c1db88847..ae7d2db4678dd 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php
@@ -13,36 +13,37 @@
use Doctrine\Common\Collections\ArrayCollection;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper;
use Symfony\Component\Form\Extension\Core\EventListener\ResizeFormListener;
+use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormEvent;
+use Symfony\Component\Form\FormFactoryBuilder;
class ResizeFormListenerTest extends TestCase
{
- private $dispatcher;
private $factory;
private $form;
protected function setUp()
{
- $this->dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock();
- $this->factory = $this->getMockBuilder('Symfony\Component\Form\FormFactoryInterface')->getMock();
+ $this->factory = (new FormFactoryBuilder())->getFormFactory();
$this->form = $this->getBuilder()
->setCompound(true)
- ->setDataMapper($this->getDataMapper())
+ ->setDataMapper(new PropertyPathMapper())
->getForm();
}
protected function tearDown()
{
- $this->dispatcher = null;
$this->factory = null;
$this->form = null;
}
protected function getBuilder($name = 'name')
{
- return new FormBuilder($name, null, $this->dispatcher, $this->factory);
+ return new FormBuilder($name, null, new EventDispatcher(), $this->factory);
}
protected function getForm($name = 'name')
@@ -50,31 +51,14 @@ protected function getForm($name = 'name')
return $this->getBuilder($name)->getForm();
}
- /**
- * @return \PHPUnit_Framework_MockObject_MockObject
- */
- private function getDataMapper()
- {
- return $this->getMockBuilder('Symfony\Component\Form\DataMapperInterface')->getMock();
- }
-
public function testPreSetDataResizesForm()
{
$this->form->add($this->getForm('0'));
$this->form->add($this->getForm('1'));
- $this->factory->expects($this->at(0))
- ->method('createNamed')
- ->with(1, 'text', null, ['property_path' => '[1]', 'attr' => ['maxlength' => 10], 'auto_initialize' => false])
- ->will($this->returnValue($this->getForm('1')));
- $this->factory->expects($this->at(1))
- ->method('createNamed')
- ->with(2, 'text', null, ['property_path' => '[2]', 'attr' => ['maxlength' => 10], 'auto_initialize' => false])
- ->will($this->returnValue($this->getForm('2')));
-
$data = [1 => 'string', 2 => 'string'];
$event = new FormEvent($this->form, $data);
- $listener = new ResizeFormListener('text', ['attr' => ['maxlength' => 10]], false, false);
+ $listener = new ResizeFormListener(TextType::class, ['attr' => ['maxlength' => 10]], false, false);
$listener->preSetData($event);
$this->assertFalse($this->form->has('0'));
@@ -95,26 +79,21 @@ public function testPreSetDataRequiresArrayOrTraversable()
public function testPreSetDataDealsWithNullData()
{
- $this->factory->expects($this->never())->method('createNamed');
-
$data = null;
$event = new FormEvent($this->form, $data);
- $listener = new ResizeFormListener('text', [], false, false);
+ $listener = new ResizeFormListener(TextType::class, [], false, false);
$listener->preSetData($event);
+
+ $this->assertSame(0, $this->form->count());
}
public function testPreSubmitResizesUpIfAllowAdd()
{
$this->form->add($this->getForm('0'));
- $this->factory->expects($this->once())
- ->method('createNamed')
- ->with(1, 'text', null, ['property_path' => '[1]', 'attr' => ['maxlength' => 10], 'auto_initialize' => false])
- ->will($this->returnValue($this->getForm('1')));
-
$data = [0 => 'string', 1 => 'string'];
$event = new FormEvent($this->form, $data);
- $listener = new ResizeFormListener('text', ['attr' => ['maxlength' => 10]], true, false);
+ $listener = new ResizeFormListener(TextType::class, ['attr' => ['maxlength' => 10]], true, false);
$listener->preSubmit($event);
$this->assertTrue($this->form->has('0'));
@@ -293,12 +272,12 @@ public function testOnSubmitDeleteEmptyCompoundEntriesIfAllowDelete()
$this->form->setData(['0' => ['name' => 'John'], '1' => ['name' => 'Jane']]);
$form1 = $this->getBuilder('0')
->setCompound(true)
- ->setDataMapper($this->getDataMapper())
+ ->setDataMapper(new PropertyPathMapper())
->getForm();
$form1->add($this->getForm('name'));
$form2 = $this->getBuilder('1')
->setCompound(true)
- ->setDataMapper($this->getDataMapper())
+ ->setDataMapper(new PropertyPathMapper())
->getForm();
$form2->add($this->getForm('name'));
$this->form->add($form1);
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/TrimListenerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/TrimListenerTest.php
index 78dae1bd82231..a61fb86a39b6e 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/TrimListenerTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/TrimListenerTest.php
@@ -12,9 +12,10 @@
namespace Symfony\Component\Form\Tests\Extension\Core\EventListener;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Form\Extension\Core\EventListener\TrimListener;
use Symfony\Component\Form\Form;
-use Symfony\Component\Form\FormConfigInterface;
+use Symfony\Component\Form\FormConfigBuilder;
use Symfony\Component\Form\FormEvent;
class TrimListenerTest extends TestCase
@@ -22,7 +23,7 @@ class TrimListenerTest extends TestCase
public function testTrim()
{
$data = ' Foo! ';
- $form = new Form($this->getMockBuilder(FormConfigInterface::class)->getMock());
+ $form = new Form(new FormConfigBuilder('name', null, new EventDispatcher()));
$event = new FormEvent($form, $data);
$filter = new TrimListener();
@@ -34,7 +35,7 @@ public function testTrim()
public function testTrimSkipNonStrings()
{
$data = 1234;
- $form = new Form($this->getMockBuilder(FormConfigInterface::class)->getMock());
+ $form = new Form(new FormConfigBuilder('name', null, new EventDispatcher()));
$event = new FormEvent($form, $data);
$filter = new TrimListener();
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/BaseTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/BaseTypeTest.php
index 92eff38669ff2..62654fc618376 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/BaseTypeTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/BaseTypeTest.php
@@ -12,12 +12,15 @@
namespace Symfony\Component\Form\Tests\Extension\Core\Type;
use Symfony\Component\Form\Test\TypeTestCase;
+use Symfony\Component\Form\Tests\VersionAwareTest;
/**
* @author Bernhard Schussek
*/
abstract class BaseTypeTest extends TypeTestCase
{
+ use VersionAwareTest;
+
const TESTED_TYPE = '';
public function testPassDisabledAsOption()
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 c1e0571ec7862..ea012c451e885 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php
@@ -14,6 +14,7 @@
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;
class FileTypeTest extends BaseTypeTest
@@ -24,9 +25,7 @@ class FileTypeTest extends BaseTypeTest
public function testSetData()
{
$form = $this->factory->createBuilder(static::TESTED_TYPE)->getForm();
- $data = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\File')
- ->setConstructorArgs([__DIR__.'/../../../Fixtures/foo', 'foo'])
- ->getMock();
+ $data = new File(__DIR__.'/../../../Fixtures/foo', false);
$form->setData($data);
@@ -40,7 +39,7 @@ public function testSetData()
public function testSubmit(RequestHandlerInterface $requestHandler)
{
$form = $this->factory->createBuilder(static::TESTED_TYPE)->setRequestHandler($requestHandler)->getForm();
- $data = $this->createUploadedFileMock($requestHandler, __DIR__.'/../../../Fixtures/foo', 'foo.jpg');
+ $data = $this->createUploadedFile($requestHandler, __DIR__.'/../../../Fixtures/foo', 'foo.jpg');
$form->submit($data);
@@ -57,8 +56,8 @@ public function testSetDataMultiple(RequestHandlerInterface $requestHandler)
])->setRequestHandler($requestHandler)->getForm();
$data = [
- $this->createUploadedFileMock($requestHandler, __DIR__.'/../../../Fixtures/foo', 'foo.jpg'),
- $this->createUploadedFileMock($requestHandler, __DIR__.'/../../../Fixtures/foo2', 'foo2.jpg'),
+ $this->createUploadedFile($requestHandler, __DIR__.'/../../../Fixtures/foo', 'foo.jpg'),
+ $this->createUploadedFile($requestHandler, __DIR__.'/../../../Fixtures/foo2', 'foo2.jpg'),
];
$form->setData($data);
@@ -75,8 +74,8 @@ public function testSubmitMultiple(RequestHandlerInterface $requestHandler)
])->setRequestHandler($requestHandler)->getForm();
$data = [
- $this->createUploadedFileMock($requestHandler, __DIR__.'/../../../Fixtures/foo', 'foo.jpg'),
- $this->createUploadedFileMock($requestHandler, __DIR__.'/../../../Fixtures/foo2', 'foo2.jpg'),
+ $this->createUploadedFile($requestHandler, __DIR__.'/../../../Fixtures/foo', 'foo.jpg'),
+ $this->createUploadedFile($requestHandler, __DIR__.'/../../../Fixtures/foo2', 'foo2.jpg'),
];
$form->submit($data);
@@ -94,7 +93,7 @@ public function testDontPassValueToView(RequestHandlerInterface $requestHandler)
{
$form = $this->factory->createBuilder(static::TESTED_TYPE)->setRequestHandler($requestHandler)->getForm();
$form->submit([
- 'file' => $this->createUploadedFileMock($requestHandler, __DIR__.'/../../../Fixtures/foo', 'foo.jpg'),
+ 'file' => $this->createUploadedFile($requestHandler, __DIR__.'/../../../Fixtures/foo', 'foo.jpg'),
]);
$this->assertEquals('', $form->createView()->vars['value']);
@@ -152,8 +151,8 @@ public function testMultipleSubmittedFilePathsAreDropped(RequestHandlerInterface
->getForm();
$form->submit([
'file:///etc/passwd',
- $this->createUploadedFileMock(new HttpFoundationRequestHandler(), __DIR__.'/../../../Fixtures/foo', 'foo.jpg'),
- $this->createUploadedFileMock(new NativeRequestHandler(), __DIR__.'/../../../Fixtures/foo2', 'foo2.jpg'),
+ $this->createUploadedFile(new HttpFoundationRequestHandler(), __DIR__.'/../../../Fixtures/foo', 'foo.jpg'),
+ $this->createUploadedFile(new NativeRequestHandler(), __DIR__.'/../../../Fixtures/foo2', 'foo2.jpg'),
]);
$this->assertCount(1, $form->getData());
@@ -185,7 +184,7 @@ public function requestHandlerProvider()
];
}
- private function createUploadedFileMock(RequestHandlerInterface $requestHandler, $path, $originalName)
+ private function createUploadedFile(RequestHandlerInterface $requestHandler, $path, $originalName)
{
if ($requestHandler instanceof HttpFoundationRequestHandler) {
return new UploadedFile($path, $originalName, null, 10, null, true);
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php
index 273402aefeb32..c5c7dd9161b7e 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php
@@ -24,14 +24,15 @@ protected function setUp()
parent::setUp();
}
- public function testSubmitCastsToInteger()
+ public function testSubmitRejectsFloats()
{
$form = $this->factory->create(static::TESTED_TYPE);
$form->submit('1.678');
- $this->assertSame(1, $form->getData());
- $this->assertSame('1', $form->getViewData());
+ $this->assertTrue($form->isSubmitted());
+ $this->assertFalse($form->isValid());
+ $this->assertFalse($form->isSynchronized());
}
public function testSubmitNull($expected = null, $norm = null, $view = null)
@@ -50,4 +51,45 @@ public function testSubmitNullUsesDefaultEmptyData($emptyData = '10', $expectedD
$this->assertSame($expectedData, $form->getNormData());
$this->assertSame($expectedData, $form->getData());
}
+
+ public function testSubmittedLargeIntegersAreNotCastToFloat()
+ {
+ if (4 === \PHP_INT_SIZE) {
+ $this->markTestSkipped('This test requires a 64bit PHP.');
+ }
+
+ $form = $this->factory->create(static::TESTED_TYPE);
+ $form->submit('201803221011791');
+
+ $this->assertSame(201803221011791, $form->getData());
+ $this->assertSame('201803221011791', $form->getViewData());
+ }
+
+ public function testTooSmallIntegersAreNotValid()
+ {
+ if (4 === \PHP_INT_SIZE) {
+ $min = '-2147483649';
+ } else {
+ $min = '-9223372036854775808';
+ }
+
+ $form = $this->factory->create(static::TESTED_TYPE);
+ $form->submit($min);
+
+ $this->assertFalse($form->isSynchronized());
+ }
+
+ public function testTooGreatIntegersAreNotValid()
+ {
+ if (4 === \PHP_INT_SIZE) {
+ $max = '2147483648';
+ } else {
+ $max = '9223372036854775808';
+ }
+
+ $form = $this->factory->create(static::TESTED_TYPE);
+ $form->submit($max);
+
+ $this->assertFalse($form->isSynchronized());
+ }
}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/CsrfValidationListenerTest.php b/src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/CsrfValidationListenerTest.php
index 43e9acad057c4..5876b092b9da0 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/CsrfValidationListenerTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/CsrfValidationListenerTest.php
@@ -12,9 +12,13 @@
namespace Symfony\Component\Form\Tests\Extension\Csrf\EventListener;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper;
use Symfony\Component\Form\Extension\Csrf\EventListener\CsrfValidationListener;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormEvent;
+use Symfony\Component\Form\FormFactoryBuilder;
+use Symfony\Component\Security\Csrf\CsrfTokenManager;
class CsrfValidationListenerTest extends TestCase
{
@@ -25,11 +29,11 @@ class CsrfValidationListenerTest extends TestCase
protected function setUp()
{
- $this->dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock();
- $this->factory = $this->getMockBuilder('Symfony\Component\Form\FormFactoryInterface')->getMock();
- $this->tokenManager = $this->getMockBuilder('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface')->getMock();
+ $this->dispatcher = new EventDispatcher();
+ $this->factory = (new FormFactoryBuilder())->getFormFactory();
+ $this->tokenManager = new CsrfTokenManager();
$this->form = $this->getBuilder()
- ->setDataMapper($this->getDataMapper())
+ ->setDataMapper(new PropertyPathMapper())
->getForm();
}
@@ -46,11 +50,6 @@ protected function getBuilder()
return new FormBuilder('post', null, $this->dispatcher, $this->factory, ['compound' => true]);
}
- protected function getDataMapper()
- {
- return $this->getMockBuilder('Symfony\Component\Form\DataMapperInterface')->getMock();
- }
-
// https://github.com/symfony/symfony/pull/5838
public function testStringFormData()
{
@@ -64,6 +63,16 @@ public function testStringFormData()
$this->assertSame($data, $event->getData());
}
+ public function testArrayCsrfToken()
+ {
+ $event = new FormEvent($this->form, ['csrf' => []]);
+
+ $validation = new CsrfValidationListener('csrf', $this->tokenManager, 'unknown', 'Invalid.');
+ $validation->preSubmit($event);
+
+ $this->assertNotEmpty($this->form->getErrors());
+ }
+
public function testMaxPostSizeExceeded()
{
$serverParams = $this
diff --git a/src/Symfony/Component/Form/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php
index b78bd67ae0f3a..375fbfc7021a1 100644
--- a/src/Symfony/Component/Form/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php
@@ -12,7 +12,8 @@
namespace Symfony\Component\Form\Tests\Extension\DependencyInjection;
use PHPUnit\Framework\TestCase;
-use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension;
use Symfony\Component\Form\FormTypeGuesserChain;
use Symfony\Component\Form\FormTypeGuesserInterface;
@@ -21,19 +22,16 @@ class DependencyInjectionExtensionTest extends TestCase
{
public function testGetTypeExtensions()
{
- $container = $this->createContainerMock();
- $container->expects($this->never())->method('get');
-
- $typeExtension1 = $this->createFormTypeExtensionMock('test');
- $typeExtension2 = $this->createFormTypeExtensionMock('test');
- $typeExtension3 = $this->createFormTypeExtensionMock('other');
+ $typeExtension1 = new DummyExtension('test');
+ $typeExtension2 = new DummyExtension('test');
+ $typeExtension3 = new DummyExtension('other');
$extensions = [
'test' => new \ArrayIterator([$typeExtension1, $typeExtension2]),
'other' => new \ArrayIterator([$typeExtension3]),
];
- $extension = new DependencyInjectionExtension($container, $extensions, []);
+ $extension = new DependencyInjectionExtension(new ContainerBuilder(), $extensions, []);
$this->assertTrue($extension->hasTypeExtensions('test'));
$this->assertTrue($extension->hasTypeExtensions('other'));
@@ -47,14 +45,11 @@ public function testGetTypeExtensions()
*/
public function testThrowExceptionForInvalidExtendedType()
{
- $container = $this->getMockBuilder('Psr\Container\ContainerInterface')->getMock();
- $container->expects($this->never())->method('get');
-
$extensions = [
- 'test' => new \ArrayIterator([$this->createFormTypeExtensionMock('unmatched')]),
+ 'test' => new \ArrayIterator([new DummyExtension('unmatched')]),
];
- $extension = new DependencyInjectionExtension($container, $extensions, []);
+ $extension = new DependencyInjectionExtension(new ContainerBuilder(), $extensions, []);
$extension->getTypeExtensions('test');
}
@@ -65,23 +60,15 @@ public function testThrowExceptionForInvalidExtendedType()
*/
public function testLegacyGetTypeExtensions()
{
- $container = $this->createContainerMock();
-
- $services = [
- 'extension1' => $typeExtension1 = $this->createFormTypeExtensionMock('test'),
- 'extension2' => $typeExtension2 = $this->createFormTypeExtensionMock('test'),
- 'extension3' => $typeExtension3 = $this->createFormTypeExtensionMock('other'),
- ];
+ $container = new ContainerBuilder();
- $container->expects($this->any())
- ->method('get')
- ->willReturnCallback(function ($id) use ($services) {
- if (isset($services[$id])) {
- return $services[$id];
- }
+ $typeExtension1 = new DummyExtension('test');
+ $typeExtension2 = new DummyExtension('test');
+ $typeExtension3 = new DummyExtension('other');
- throw new ServiceNotFoundException($id);
- });
+ $container->set('extension1', $typeExtension1);
+ $container->set('extension2', $typeExtension2);
+ $container->set('extension3', $typeExtension3);
$extension = new DependencyInjectionExtension($container, [], ['test' => ['extension1', 'extension2'], 'other' => ['extension3']], []);
@@ -97,14 +84,10 @@ public function testLegacyGetTypeExtensions()
*/
public function testLegacyThrowExceptionForInvalidExtendedType()
{
- $formTypeExtension = $this->createFormTypeExtensionMock('unmatched');
-
- $container = $this->createContainerMock();
+ $formTypeExtension = new DummyExtension('unmatched');
- $container->expects($this->any())
- ->method('get')
- ->with('extension')
- ->willReturn($formTypeExtension);
+ $container = new ContainerBuilder();
+ $container->set('extension', $formTypeExtension);
$extension = new DependencyInjectionExtension($container, [], ['test' => ['extension']], []);
@@ -116,16 +99,14 @@ public function testLegacyThrowExceptionForInvalidExtendedType()
public function testGetTypeGuesser()
{
- $container = $this->createContainerMock();
- $extension = new DependencyInjectionExtension($container, [], [$this->getMockBuilder(FormTypeGuesserInterface::class)->getMock()]);
+ $extension = new DependencyInjectionExtension(new ContainerBuilder(), [], [$this->getMockBuilder(FormTypeGuesserInterface::class)->getMock()]);
$this->assertInstanceOf(FormTypeGuesserChain::class, $extension->getTypeGuesser());
}
public function testGetTypeGuesserReturnsNullWhenNoTypeGuessersHaveBeenConfigured()
{
- $container = $this->createContainerMock();
- $extension = new DependencyInjectionExtension($container, [], []);
+ $extension = new DependencyInjectionExtension(new ContainerBuilder(), [], []);
$this->assertNull($extension->getTypeGuesser());
}
@@ -135,12 +116,9 @@ public function testGetTypeGuesserReturnsNullWhenNoTypeGuessersHaveBeenConfigure
*/
public function testLegacyGetTypeGuesser()
{
- $container = $this->createContainerMock();
- $container
- ->expects($this->once())
- ->method('get')
- ->with('foo')
- ->willReturn($this->getMockBuilder(FormTypeGuesserInterface::class)->getMock());
+ $container = new ContainerBuilder();
+ $container->set('foo', new DummyTypeGuesser());
+
$extension = new DependencyInjectionExtension($container, [], [], ['foo']);
$this->assertInstanceOf(FormTypeGuesserChain::class, $extension->getTypeGuesser());
@@ -151,24 +129,42 @@ public function testLegacyGetTypeGuesser()
*/
public function testLegacyGetTypeGuesserReturnsNullWhenNoTypeGuessersHaveBeenConfigured()
{
- $container = $this->createContainerMock();
- $extension = new DependencyInjectionExtension($container, [], [], []);
+ $extension = new DependencyInjectionExtension(new ContainerBuilder(), [], [], []);
$this->assertNull($extension->getTypeGuesser());
}
+}
+
+class DummyExtension extends AbstractTypeExtension
+{
+ private $extendedType;
+
+ public function __construct($extendedType)
+ {
+ $this->extendedType = $extendedType;
+ }
- private function createContainerMock()
+ public function getExtendedType()
{
- return $this->getMockBuilder('Psr\Container\ContainerInterface')
- ->setMethods(['get', 'has'])
- ->getMock();
+ return $this->extendedType;
}
+}
- private function createFormTypeExtensionMock($extendedType)
+class DummyTypeGuesser implements FormTypeGuesserInterface
+{
+ public function guessType($class, $property)
{
- $extension = $this->getMockBuilder('Symfony\Component\Form\FormTypeExtensionInterface')->getMock();
- $extension->expects($this->any())->method('getExtendedType')->willReturn($extendedType);
+ }
- return $extension;
+ public function guessRequired($class, $property)
+ {
+ }
+
+ public function guessMaxLength($class, $property)
+ {
+ }
+
+ public function guessPattern($class, $property)
+ {
}
}
diff --git a/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php b/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php
index 5597e835542df..2b134511830e7 100644
--- a/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php
@@ -47,7 +47,7 @@ protected function getRequestHandler()
return new HttpFoundationRequestHandler($this->serverParams);
}
- protected function getMockFile($suffix = '')
+ protected function getUploadedFile($suffix = '')
{
return new UploadedFile(__DIR__.'/../../Fixtures/foo'.$suffix, 'foo'.$suffix);
}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php
index 399f2d70cafb5..7581f7698c12c 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php
@@ -11,18 +11,26 @@
namespace Symfony\Component\Form\Tests\Extension\Validator\Constraints;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\Exception\TransformationFailedException;
+use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper;
use Symfony\Component\Form\Extension\Validator\Constraints\Form;
use Symfony\Component\Form\Extension\Validator\Constraints\FormValidator;
use Symfony\Component\Form\FormBuilder;
+use Symfony\Component\Form\FormFactoryBuilder;
+use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\SubmitButtonBuilder;
+use Symfony\Component\Translation\IdentityTranslator;
use Symfony\Component\Validator\Constraints\GroupSequence;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\NotNull;
use Symfony\Component\Validator\Constraints\Valid;
+use Symfony\Component\Validator\Context\ExecutionContext;
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
+use Symfony\Component\Validator\Validation;
/**
* @author Bernhard Schussek
@@ -30,43 +38,31 @@
class FormValidatorTest extends ConstraintValidatorTestCase
{
/**
- * @var \PHPUnit_Framework_MockObject_MockObject
+ * @var EventDispatcherInterface
*/
private $dispatcher;
/**
- * @var \PHPUnit_Framework_MockObject_MockObject
+ * @var FormFactoryInterface
*/
private $factory;
- /**
- * @var \PHPUnit_Framework_MockObject_MockObject
- */
- protected $serverParams;
-
protected function setUp()
{
- $this->dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock();
- $this->factory = $this->getMockBuilder('Symfony\Component\Form\FormFactoryInterface')->getMock();
- $this->serverParams = $this->getMockBuilder('Symfony\Component\Form\Extension\Validator\Util\ServerParams')->setMethods(['getNormalizedIniPostMaxSize', 'getContentLength'])->getMock();
+ $this->dispatcher = new EventDispatcher();
+ $this->factory = (new FormFactoryBuilder())->getFormFactory();
parent::setUp();
$this->constraint = new Form();
}
- protected function createValidator()
- {
- return new FormValidator($this->serverParams);
- }
-
public function testValidate()
{
- $object = $this->getMockBuilder('\stdClass')->getMock();
+ $object = new \stdClass();
$options = ['validation_groups' => ['group1', 'group2']];
- $form = $this->getBuilder('name', '\stdClass', $options)
- ->setData($object)
- ->getForm();
+ $form = $this->getCompoundForm($object, $options);
+ $form->submit([]);
$this->expectValidateAt(0, 'data', $object, ['group1', 'group2']);
@@ -77,7 +73,7 @@ public function testValidate()
public function testValidateConstraints()
{
- $object = $this->getMockBuilder('\stdClass')->getMock();
+ $object = new \stdClass();
$constraint1 = new NotNull(['groups' => ['group1', 'group2']]);
$constraint2 = new NotBlank(['groups' => 'group2']);
@@ -85,9 +81,8 @@ public function testValidateConstraints()
'validation_groups' => ['group1', 'group2'],
'constraints' => [$constraint1, $constraint2],
];
- $form = $this->getBuilder('name', '\stdClass', $options)
- ->setData($object)
- ->getForm();
+ $form = $this->getCompoundForm($object, $options);
+ $form->submit([]);
// First default constraints
$this->expectValidateAt(0, 'data', $object, ['group1', 'group2']);
@@ -103,20 +98,19 @@ public function testValidateConstraints()
public function testValidateChildIfValidConstraint()
{
- $object = $this->getMockBuilder('\stdClass')->getMock();
+ $object = new \stdClass();
$parent = $this->getBuilder('parent')
->setCompound(true)
- ->setDataMapper($this->getDataMapper())
+ ->setDataMapper(new PropertyPathMapper())
->getForm();
$options = [
'validation_groups' => ['group1', 'group2'],
'constraints' => [new Valid()],
];
- $form = $this->getBuilder('name', '\stdClass', $options)->getForm();
+ $form = $this->getCompoundForm($object, $options);
$parent->add($form);
-
- $form->setData($object);
+ $parent->submit([]);
$this->expectValidateAt(0, 'data', $object, ['group1', 'group2']);
@@ -127,11 +121,11 @@ public function testValidateChildIfValidConstraint()
public function testDontValidateIfParentWithoutValidConstraint()
{
- $object = $this->getMockBuilder('\stdClass')->getMock();
+ $object = new \stdClass();
$parent = $this->getBuilder('parent', null)
->setCompound(true)
- ->setDataMapper($this->getDataMapper())
+ ->setDataMapper(new PropertyPathMapper())
->getForm();
$options = ['validation_groups' => ['group1', 'group2']];
$form = $this->getBuilder('name', '\stdClass', $options)->getForm();
@@ -149,8 +143,8 @@ public function testDontValidateIfParentWithoutValidConstraint()
public function testMissingConstraintIndex()
{
$object = new \stdClass();
- $form = new FormBuilder('name', '\stdClass', $this->dispatcher, $this->factory);
- $form = $form->setData($object)->getForm();
+ $form = $this->getCompoundForm($object);
+ $form->submit([]);
$this->expectValidateAt(0, 'data', $object, ['Default']);
@@ -161,22 +155,21 @@ public function testMissingConstraintIndex()
public function testValidateConstraintsOptionEvenIfNoValidConstraint()
{
- $object = $this->getMockBuilder('\stdClass')->getMock();
+ $object = new \stdClass();
$constraint1 = new NotNull(['groups' => ['group1', 'group2']]);
$constraint2 = new NotBlank(['groups' => 'group2']);
$parent = $this->getBuilder('parent', null)
->setCompound(true)
- ->setDataMapper($this->getDataMapper())
+ ->setDataMapper(new PropertyPathMapper())
->getForm();
$options = [
'validation_groups' => ['group1', 'group2'],
'constraints' => [$constraint1, $constraint2],
];
- $form = $this->getBuilder('name', '\stdClass', $options)
- ->setData($object)
- ->getForm();
+ $form = $this->getCompoundForm($object, $options);
$parent->add($form);
+ $parent->submit([]);
$this->expectValidateValueAt(0, 'data', $object, $constraint1, 'group1');
$this->expectValidateValueAt(1, 'data', $object, $constraint2, 'group2');
@@ -188,7 +181,7 @@ public function testValidateConstraintsOptionEvenIfNoValidConstraint()
public function testDontValidateIfNoValidationGroups()
{
- $object = $this->getMockBuilder('\stdClass')->getMock();
+ $object = new \stdClass();
$form = $this->getBuilder('name', '\stdClass', [
'validation_groups' => [],
@@ -207,13 +200,11 @@ public function testDontValidateIfNoValidationGroups()
public function testDontValidateConstraintsIfNoValidationGroups()
{
- $object = $this->getMockBuilder('\stdClass')->getMock();
- $constraint1 = $this->getMockBuilder('Symfony\Component\Validator\Constraint')->getMock();
- $constraint2 = $this->getMockBuilder('Symfony\Component\Validator\Constraint')->getMock();
+ $object = new \stdClass();
$options = [
'validation_groups' => [],
- 'constraints' => [$constraint1, $constraint2],
+ 'constraints' => [new NotBlank(), new NotNull()],
];
$form = $this->getBuilder('name', '\stdClass', $options)
->setData($object)
@@ -231,7 +222,7 @@ public function testDontValidateConstraintsIfNoValidationGroups()
public function testDontValidateIfNotSynchronized()
{
- $object = $this->getMockBuilder('\stdClass')->getMock();
+ $object = new \stdClass();
$form = $this->getBuilder('name', '\stdClass', [
'invalid_message' => 'invalid_message_key',
@@ -265,7 +256,7 @@ function () { throw new TransformationFailedException(); }
public function testAddInvalidErrorEvenIfNoValidationGroups()
{
- $object = $this->getMockBuilder('\stdClass')->getMock();
+ $object = new \stdClass();
$form = $this->getBuilder('name', '\stdClass', [
'invalid_message' => 'invalid_message_key',
@@ -300,14 +291,12 @@ function () { throw new TransformationFailedException(); }
public function testDontValidateConstraintsIfNotSynchronized()
{
- $object = $this->getMockBuilder('\stdClass')->getMock();
- $constraint1 = $this->getMockBuilder('Symfony\Component\Validator\Constraint')->getMock();
- $constraint2 = $this->getMockBuilder('Symfony\Component\Validator\Constraint')->getMock();
+ $object = new \stdClass();
$options = [
'invalid_message' => 'invalid_message_key',
'validation_groups' => ['group1', 'group2'],
- 'constraints' => [$constraint1, $constraint2],
+ 'constraints' => [new NotBlank(), new NotBlank()],
];
$form = $this->getBuilder('name', '\stdClass', $options)
->setData($object)
@@ -335,7 +324,8 @@ function () { throw new TransformationFailedException(); }
// https://github.com/symfony/symfony/issues/4359
public function testDontMarkInvalidIfAnyChildIsNotSynchronized()
{
- $object = $this->getMockBuilder('\stdClass')->getMock();
+ $object = new \stdClass();
+ $object->child = 'bar';
$failingTransformer = new CallbackTransformer(
function ($data) { return $data; },
@@ -346,7 +336,7 @@ function () { throw new TransformationFailedException(); }
->setData($object)
->addViewTransformer($failingTransformer)
->setCompound(true)
- ->setDataMapper($this->getDataMapper())
+ ->setDataMapper(new PropertyPathMapper())
->add(
$this->getBuilder('child')
->addViewTransformer($failingTransformer)
@@ -365,11 +355,10 @@ function () { throw new TransformationFailedException(); }
public function testHandleGroupSequenceValidationGroups()
{
- $object = $this->getMockBuilder('\stdClass')->getMock();
+ $object = new \stdClass();
$options = ['validation_groups' => new GroupSequence(['group1', 'group2'])];
- $form = $this->getBuilder('name', '\stdClass', $options)
- ->setData($object)
- ->getForm();
+ $form = $this->getCompoundForm($object, $options);
+ $form->submit([]);
$this->expectValidateAt(0, 'data', $object, new GroupSequence(['group1', 'group2']));
$this->expectValidateAt(1, 'data', $object, new GroupSequence(['group1', 'group2']));
@@ -381,11 +370,10 @@ public function testHandleGroupSequenceValidationGroups()
public function testHandleCallbackValidationGroups()
{
- $object = $this->getMockBuilder('\stdClass')->getMock();
+ $object = new \stdClass();
$options = ['validation_groups' => [$this, 'getValidationGroups']];
- $form = $this->getBuilder('name', '\stdClass', $options)
- ->setData($object)
- ->getForm();
+ $form = $this->getCompoundForm($object, $options);
+ $form->submit([]);
$this->expectValidateAt(0, 'data', $object, ['group1', 'group2']);
@@ -396,11 +384,10 @@ public function testHandleCallbackValidationGroups()
public function testDontExecuteFunctionNames()
{
- $object = $this->getMockBuilder('\stdClass')->getMock();
+ $object = new \stdClass();
$options = ['validation_groups' => 'header'];
- $form = $this->getBuilder('name', '\stdClass', $options)
- ->setData($object)
- ->getForm();
+ $form = $this->getCompoundForm($object, $options);
+ $form->submit([]);
$this->expectValidateAt(0, 'data', $object, ['header']);
@@ -411,13 +398,12 @@ public function testDontExecuteFunctionNames()
public function testHandleClosureValidationGroups()
{
- $object = $this->getMockBuilder('\stdClass')->getMock();
+ $object = new \stdClass();
$options = ['validation_groups' => function (FormInterface $form) {
return ['group1', 'group2'];
}];
- $form = $this->getBuilder('name', '\stdClass', $options)
- ->setData($object)
- ->getForm();
+ $form = $this->getCompoundForm($object, $options);
+ $form->submit([]);
$this->expectValidateAt(0, 'data', $object, ['group1', 'group2']);
@@ -428,11 +414,11 @@ public function testHandleClosureValidationGroups()
public function testUseValidationGroupOfClickedButton()
{
- $object = $this->getMockBuilder('\stdClass')->getMock();
+ $object = new \stdClass();
$parent = $this->getBuilder('parent')
->setCompound(true)
- ->setDataMapper($this->getDataMapper())
+ ->setDataMapper(new PropertyPathMapper())
->getForm();
$form = $this->getForm('name', '\stdClass', [
'validation_groups' => 'form_group',
@@ -455,13 +441,13 @@ public function testUseValidationGroupOfClickedButton()
public function testDontUseValidationGroupOfUnclickedButton()
{
- $object = $this->getMockBuilder('\stdClass')->getMock();
+ $object = new \stdClass();
$parent = $this->getBuilder('parent')
->setCompound(true)
- ->setDataMapper($this->getDataMapper())
+ ->setDataMapper(new PropertyPathMapper())
->getForm();
- $form = $this->getForm('name', '\stdClass', [
+ $form = $this->getCompoundForm($object, [
'validation_groups' => 'form_group',
'constraints' => [new Valid()],
]);
@@ -471,7 +457,7 @@ public function testDontUseValidationGroupOfUnclickedButton()
'validation_groups' => 'button_group',
]));
- $form->setData($object);
+ $parent->submit([]);
$this->expectValidateAt(0, 'data', $object, ['form_group']);
@@ -482,18 +468,17 @@ public function testDontUseValidationGroupOfUnclickedButton()
public function testUseInheritedValidationGroup()
{
- $object = $this->getMockBuilder('\stdClass')->getMock();
+ $object = new \stdClass();
$parentOptions = ['validation_groups' => 'group'];
$parent = $this->getBuilder('parent', null, $parentOptions)
->setCompound(true)
- ->setDataMapper($this->getDataMapper())
+ ->setDataMapper(new PropertyPathMapper())
->getForm();
$formOptions = ['constraints' => [new Valid()]];
- $form = $this->getBuilder('name', '\stdClass', $formOptions)->getForm();
+ $form = $this->getCompoundForm($object, $formOptions);
$parent->add($form);
-
- $form->setData($object);
+ $parent->submit([]);
$this->expectValidateAt(0, 'data', $object, ['group']);
@@ -504,18 +489,17 @@ public function testUseInheritedValidationGroup()
public function testUseInheritedCallbackValidationGroup()
{
- $object = $this->getMockBuilder('\stdClass')->getMock();
+ $object = new \stdClass();
$parentOptions = ['validation_groups' => [$this, 'getValidationGroups']];
$parent = $this->getBuilder('parent', null, $parentOptions)
->setCompound(true)
- ->setDataMapper($this->getDataMapper())
+ ->setDataMapper(new PropertyPathMapper())
->getForm();
$formOptions = ['constraints' => [new Valid()]];
- $form = $this->getBuilder('name', '\stdClass', $formOptions)->getForm();
+ $form = $this->getCompoundForm($object, $formOptions);
$parent->add($form);
-
- $form->setData($object);
+ $parent->submit([]);
$this->expectValidateAt(0, 'data', $object, ['group1', 'group2']);
@@ -526,22 +510,21 @@ public function testUseInheritedCallbackValidationGroup()
public function testUseInheritedClosureValidationGroup()
{
- $object = $this->getMockBuilder('\stdClass')->getMock();
+ $object = new \stdClass();
$parentOptions = [
- 'validation_groups' => function (FormInterface $form) {
+ 'validation_groups' => function () {
return ['group1', 'group2'];
},
];
$parent = $this->getBuilder('parent', null, $parentOptions)
->setCompound(true)
- ->setDataMapper($this->getDataMapper())
+ ->setDataMapper(new PropertyPathMapper())
->getForm();
$formOptions = ['constraints' => [new Valid()]];
- $form = $this->getBuilder('name', '\stdClass', $formOptions)->getForm();
+ $form = $this->getCompoundForm($object, $formOptions);
$parent->add($form);
-
- $form->setData($object);
+ $parent->submit([]);
$this->expectValidateAt(0, 'data', $object, ['group1', 'group2']);
@@ -552,10 +535,9 @@ public function testUseInheritedClosureValidationGroup()
public function testAppendPropertyPath()
{
- $object = $this->getMockBuilder('\stdClass')->getMock();
- $form = $this->getBuilder('name', '\stdClass')
- ->setData($object)
- ->getForm();
+ $object = new \stdClass();
+ $form = $this->getCompoundForm($object);
+ $form->submit([]);
$this->expectValidateAt(0, 'data', $object, ['Default']);
@@ -581,7 +563,7 @@ public function testViolationIfExtraData()
{
$form = $this->getBuilder('parent', null, ['extra_fields_message' => 'Extra!'])
->setCompound(true)
- ->setDataMapper($this->getDataMapper())
+ ->setDataMapper(new PropertyPathMapper())
->add($this->getBuilder('child'))
->getForm();
@@ -602,7 +584,7 @@ public function testViolationFormatIfMultipleExtraFields()
{
$form = $this->getBuilder('parent', null, ['extra_fields_message' => 'Extra!'])
->setCompound(true)
- ->setDataMapper($this->getDataMapper())
+ ->setDataMapper(new PropertyPathMapper())
->add($this->getBuilder('child'))
->getForm();
@@ -621,22 +603,21 @@ public function testViolationFormatIfMultipleExtraFields()
public function testNoViolationIfAllowExtraData()
{
- $context = $this->getMockExecutionContext();
-
$form = $this
->getBuilder('parent', null, ['allow_extra_fields' => true])
->setCompound(true)
- ->setDataMapper($this->getDataMapper())
+ ->setDataMapper(new PropertyPathMapper())
->add($this->getBuilder('child'))
->getForm();
- $form->submit(['foo' => 'bar']);
+ $context = new ExecutionContext(Validation::createValidator(), $form, new IdentityTranslator());
- $context->expects($this->never())
- ->method('addViolation');
+ $form->submit(['foo' => 'bar']);
$this->validator->initialize($context);
$this->validator->validate($form, new Form());
+
+ $this->assertCount(0, $context->getViolations());
}
/**
@@ -649,22 +630,30 @@ public function getValidationGroups(FormInterface $form)
return ['group1', 'group2'];
}
- private function getMockExecutionContext()
+ public function testCauseForNotAllowedExtraFieldsIsTheFormConstraint()
{
- $context = $this->getMockBuilder('Symfony\Component\Validator\Context\ExecutionContextInterface')->getMock();
- $validator = $this->getMockBuilder('Symfony\Component\Validator\Validator\ValidatorInterface')->getMock();
- $contextualValidator = $this->getMockBuilder('Symfony\Component\Validator\Validator\ContextualValidatorInterface')->getMock();
+ $form = $this
+ ->getBuilder('form', null, ['constraints' => [new NotBlank(['groups' => ['foo']])]])
+ ->setCompound(true)
+ ->setDataMapper(new PropertyPathMapper())
+ ->getForm();
+ $form->submit([
+ 'extra_data' => 'foo',
+ ]);
- $validator->expects($this->any())
- ->method('inContext')
- ->with($context)
- ->will($this->returnValue($contextualValidator));
+ $context = new ExecutionContext(Validation::createValidator(), $form, new IdentityTranslator());
+ $constraint = new Form();
- $context->expects($this->any())
- ->method('getValidator')
- ->will($this->returnValue($validator));
+ $this->validator->initialize($context);
+ $this->validator->validate($form, $constraint);
- return $context;
+ $this->assertCount(1, $context->getViolations());
+ $this->assertSame($constraint, $context->getViolations()->get(0)->getConstraint());
+ }
+
+ protected function createValidator()
+ {
+ return new FormValidator();
}
/**
@@ -689,18 +678,19 @@ private function getForm($name = 'name', $dataClass = null, array $options = [])
return $this->getBuilder($name, $dataClass, $options)->getForm();
}
+ private function getCompoundForm($data, array $options = [])
+ {
+ return $this->getBuilder('name', \get_class($data), $options)
+ ->setData($data)
+ ->setCompound(true)
+ ->setDataMapper(new PropertyPathMapper())
+ ->getForm();
+ }
+
private function getSubmitButton($name = 'name', array $options = [])
{
$builder = new SubmitButtonBuilder($name, $options);
return $builder->getForm();
}
-
- /**
- * @return \PHPUnit_Framework_MockObject_MockObject
- */
- private function getDataMapper()
- {
- return $this->getMockBuilder('Symfony\Component\Form\DataMapperInterface')->getMock();
- }
}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/ValidationListenerTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/ValidationListenerTest.php
index 855a90d2e3479..76bc07b2ee981 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/ValidationListenerTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/ValidationListenerTest.php
@@ -12,40 +12,41 @@
namespace Symfony\Component\Form\Tests\Extension\Validator\EventListener;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper;
use Symfony\Component\Form\Extension\Validator\Constraints\Form as FormConstraint;
use Symfony\Component\Form\Extension\Validator\EventListener\ValidationListener;
+use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapper;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormBuilder;
+use Symfony\Component\Form\FormConfigBuilder;
use Symfony\Component\Form\FormEvent;
+use Symfony\Component\Form\FormFactoryBuilder;
use Symfony\Component\Form\FormFactoryInterface;
-use Symfony\Component\PropertyAccess\PropertyPath;
use Symfony\Component\Validator\ConstraintViolation;
-use Symfony\Component\Validator\ConstraintViolationList;
+use Symfony\Component\Validator\ConstraintViolationInterface;
+use Symfony\Component\Validator\Context\ExecutionContextInterface;
+use Symfony\Component\Validator\Validation;
+use Symfony\Component\Validator\Validator\ValidatorInterface;
class ValidationListenerTest extends TestCase
{
/**
- * @var \PHPUnit_Framework_MockObject_MockObject
+ * @var EventDispatcherInterface
*/
private $dispatcher;
/**
- * @var \PHPUnit_Framework_MockObject_MockObject
+ * @var FormFactoryInterface
*/
private $factory;
/**
- * @var \PHPUnit_Framework_MockObject_MockObject
+ * @var ValidatorInterface
*/
private $validator;
- /**
- * @var \PHPUnit_Framework_MockObject_MockObject
- */
- private $violationMapper;
-
/**
* @var ValidationListener
*/
@@ -59,40 +60,18 @@ class ValidationListenerTest extends TestCase
protected function setUp()
{
- $this->dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock();
- $this->factory = $this->getMockBuilder('Symfony\Component\Form\FormFactoryInterface')->getMock();
- $this->validator = $this->getMockBuilder('Symfony\Component\Validator\Validator\ValidatorInterface')->getMock();
- $this->violationMapper = $this->getMockBuilder('Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapperInterface')->getMock();
- $this->listener = new ValidationListener($this->validator, $this->violationMapper);
+ $this->dispatcher = new EventDispatcher();
+ $this->factory = (new FormFactoryBuilder())->getFormFactory();
+ $this->validator = Validation::createValidator();
+ $this->listener = new ValidationListener($this->validator, new ViolationMapper());
$this->message = 'Message';
$this->messageTemplate = 'Message template';
$this->params = ['foo' => 'bar'];
}
- private function getConstraintViolation($code = null)
- {
- return new ConstraintViolation($this->message, $this->messageTemplate, $this->params, null, 'prop.path', null, null, $code, new FormConstraint());
- }
-
- private function getBuilder($name = 'name', $propertyPath = null, $dataClass = null)
- {
- $builder = new FormBuilder($name, $dataClass, $this->dispatcher, $this->factory);
- $builder->setPropertyPath(new PropertyPath($propertyPath ?: $name));
- $builder->setAttribute('error_mapping', []);
- $builder->setErrorBubbling(false);
- $builder->setMapped(true);
-
- return $builder;
- }
-
- private function getForm($name = 'name', $propertyPath = null, $dataClass = null)
- {
- return $this->getBuilder($name, $propertyPath, $dataClass)->getForm();
- }
-
private function createForm($name = '', $compound = false)
{
- $config = new FormBuilder($name, null, $this->getMockBuilder(EventDispatcherInterface::class)->getMock(), $this->getMockBuilder(FormFactoryInterface::class)->getMock());
+ $config = new FormBuilder($name, null, new EventDispatcher(), (new FormFactoryBuilder())->getFormFactory());
$config->setCompound($compound);
if ($compound) {
@@ -105,35 +84,29 @@ private function createForm($name = '', $compound = false)
// More specific mapping tests can be found in ViolationMapperTest
public function testMapViolation()
{
- $violation = $this->getConstraintViolation();
- $form = $this->getForm('street');
+ $violation = new ConstraintViolation($this->message, $this->messageTemplate, $this->params, null, 'data', null, null, null, new FormConstraint());
+ $form = new Form(new FormConfigBuilder('street', null, new EventDispatcher()));
+ $form->submit(null);
- $this->validator->expects($this->once())
- ->method('validate')
- ->will($this->returnValue([$violation]));
+ $validator = new DummyValidator($violation);
+ $listener = new ValidationListener($validator, new ViolationMapper());
+ $listener->validateForm(new FormEvent($form, null));
- $this->violationMapper->expects($this->once())
- ->method('mapViolation')
- ->with($violation, $form, false);
-
- $this->listener->validateForm(new FormEvent($form, null));
+ $this->assertCount(1, $form->getErrors());
+ $this->assertSame($violation, $form->getErrors()[0]->getCause());
}
public function testMapViolationAllowsNonSyncIfInvalid()
{
- $violation = $this->getConstraintViolation(FormConstraint::NOT_SYNCHRONIZED_ERROR);
- $form = $this->getForm('street');
+ $violation = new ConstraintViolation($this->message, $this->messageTemplate, $this->params, null, 'data', null, null, FormConstraint::NOT_SYNCHRONIZED_ERROR, new FormConstraint());
+ $form = new SubmittedNotSynchronizedForm(new FormConfigBuilder('street', null, new EventDispatcher()));
- $this->validator->expects($this->once())
- ->method('validate')
- ->will($this->returnValue([$violation]));
+ $validator = new DummyValidator($violation);
+ $listener = new ValidationListener($validator, new ViolationMapper());
+ $listener->validateForm(new FormEvent($form, null));
- $this->violationMapper->expects($this->once())
- ->method('mapViolation')
- // pass true now
- ->with($violation, $form, true);
-
- $this->listener->validateForm(new FormEvent($form, null));
+ $this->assertCount(1, $form->getErrors());
+ $this->assertSame($violation, $form->getErrors()[0]->getCause());
}
public function testValidateIgnoresNonRoot()
@@ -143,36 +116,72 @@ public function testValidateIgnoresNonRoot()
$form = $this->createForm('', true);
$form->add($childForm);
- $this->validator->expects($this->never())
- ->method('validate');
-
- $this->violationMapper->expects($this->never())
- ->method('mapViolation');
+ $form->submit(['child' => null]);
$this->listener->validateForm(new FormEvent($childForm, null));
+
+ $this->assertTrue($childForm->isValid());
}
public function testValidateWithEmptyViolationList()
{
$form = $this->createForm();
+ $form->submit(null);
- $this->validator
- ->expects($this->once())
- ->method('validate')
- ->will($this->returnValue(new ConstraintViolationList()));
+ $this->listener->validateForm(new FormEvent($form, null));
- $this->violationMapper
- ->expects($this->never())
- ->method('mapViolation');
+ $this->assertTrue($form->isValid());
+ }
+}
- $this->listener->validateForm(new FormEvent($form, null));
+class SubmittedNotSynchronizedForm extends Form
+{
+ public function isSubmitted()
+ {
+ return true;
}
- public function testValidatorInterface()
+ public function isSynchronized()
{
- $validator = $this->getMockBuilder('Symfony\Component\Validator\Validator\ValidatorInterface')->getMock();
+ return false;
+ }
+}
+
+class DummyValidator implements ValidatorInterface
+{
+ private $violation;
- $listener = new ValidationListener($validator, $this->violationMapper);
- $this->assertAttributeSame($validator, 'validator', $listener);
+ public function __construct(ConstraintViolationInterface $violation)
+ {
+ $this->violation = $violation;
+ }
+
+ public function getMetadataFor($value)
+ {
+ }
+
+ public function hasMetadataFor($value)
+ {
+ }
+
+ public function validate($value, $constraints = null, $groups = null)
+ {
+ return [$this->violation];
+ }
+
+ public function validateProperty($object, $propertyName, $groups = null)
+ {
+ }
+
+ public function validatePropertyValue($objectOrClass, $propertyName, $value, $groups = null)
+ {
+ }
+
+ public function startContext()
+ {
+ }
+
+ public function inContext(ExecutionContextInterface $context)
+ {
}
}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php
index 8437d6f0975e8..53d29a19112c7 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php
@@ -11,7 +11,6 @@
namespace Symfony\Component\Form\Tests\Extension\Validator\Type;
-use Symfony\Component\Form\Extension\Validator\Type\FormTypeValidatorExtension;
use Symfony\Component\Form\Extension\Validator\ValidatorExtension;
use Symfony\Component\Form\Forms;
use Symfony\Component\Form\Test\Traits\ValidatorExtensionTrait;
@@ -56,14 +55,6 @@ public function testValidConstraint()
$this->assertSame([$valid], $form->getConfig()->getOption('constraints'));
}
- public function testValidatorInterface()
- {
- $validator = $this->getMockBuilder('Symfony\Component\Validator\Validator\ValidatorInterface')->getMock();
-
- $formTypeValidatorExtension = new FormTypeValidatorExtension($validator);
- $this->assertAttributeSame($validator, 'validator', $formTypeValidatorExtension);
- }
-
public function testGroupSequenceWithConstraintsOption()
{
$form = Forms::createFormFactoryBuilder()
diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/UploadValidatorExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/UploadValidatorExtensionTest.php
index 7d4178bc83ea6..c4d8e8d948610 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/UploadValidatorExtensionTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/UploadValidatorExtensionTest.php
@@ -15,23 +15,17 @@
use Symfony\Component\Form\Test\TypeTestCase;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
+use Symfony\Component\Translation\TranslatorInterface;
class UploadValidatorExtensionTest extends TypeTestCase
{
public function testPostMaxSizeTranslation()
{
- $translator = $this->getMockBuilder('Symfony\Component\Translation\TranslatorInterface')->getMock();
-
- $translator->expects($this->any())
- ->method('trans')
- ->with($this->equalTo('old max {{ max }}!'))
- ->willReturn('translated max {{ max }}!');
-
- $extension = new UploadValidatorExtension($translator);
+ $extension = new UploadValidatorExtension(new DummyTranslator());
$resolver = new OptionsResolver();
$resolver->setDefault('post_max_size_message', 'old max {{ max }}!');
- $resolver->setDefault('upload_max_size_message', function (Options $options, $message) {
+ $resolver->setDefault('upload_max_size_message', function (Options $options) {
return function () use ($options) {
return $options['post_max_size_message'];
};
@@ -43,3 +37,25 @@ public function testPostMaxSizeTranslation()
$this->assertEquals('translated max {{ max }}!', \call_user_func($options['upload_max_size_message']));
}
}
+
+class DummyTranslator implements TranslatorInterface
+{
+ public function trans($id, array $parameters = [], $domain = null, $locale = null)
+ {
+ return 'translated max {{ max }}!';
+ }
+
+ public function transChoice($id, $number, array $parameters = [], $domain = null, $locale = null)
+ {
+ return 'translated max {{ max }}!';
+ }
+
+ public function setLocale($locale)
+ {
+ }
+
+ public function getLocale()
+ {
+ return 'en';
+ }
+}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Util/ServerParamsTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Util/ServerParamsTest.php
index 0803e18de9e83..049876246c507 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/Util/ServerParamsTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Util/ServerParamsTest.php
@@ -14,6 +14,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\Form\Extension\Validator\Util\ServerParams;
use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\RequestStack;
class ServerParamsTest extends TestCase
{
@@ -32,8 +33,8 @@ public function testGetContentLengthFromSuperglobals()
public function testGetContentLengthFromRequest()
{
$request = Request::create('http://foo', 'GET', [], [], [], ['CONTENT_LENGTH' => 1024]);
- $requestStack = $this->getMockBuilder('Symfony\Component\HttpFoundation\RequestStack')->setMethods(['getCurrentRequest'])->getMock();
- $requestStack->expects($this->once())->method('getCurrentRequest')->will($this->returnValue($request));
+ $requestStack = new RequestStack();
+ $requestStack->push($request);
$serverParams = new ServerParams($requestStack);
$this->assertEquals(1024, $serverParams->getContentLength());
@@ -42,11 +43,7 @@ public function testGetContentLengthFromRequest()
/** @dataProvider getGetPostMaxSizeTestData */
public function testGetPostMaxSize($size, $bytes)
{
- $serverParams = $this->getMockBuilder('Symfony\Component\Form\Extension\Validator\Util\ServerParams')->setMethods(['getNormalizedIniPostMaxSize'])->getMock();
- $serverParams
- ->expects($this->any())
- ->method('getNormalizedIniPostMaxSize')
- ->will($this->returnValue(strtoupper($size)));
+ $serverParams = new DummyServerParams($size);
$this->assertEquals($bytes, $serverParams->getPostMaxSize());
}
@@ -70,3 +67,20 @@ public function getGetPostMaxSizeTestData()
];
}
}
+
+class DummyServerParams extends ServerParams
+{
+ private $size;
+
+ public function __construct($size)
+ {
+ parent::__construct();
+
+ $this->size = $size;
+ }
+
+ public function getNormalizedIniPostMaxSize()
+ {
+ return $this->size;
+ }
+}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php
index ce18d0b6c5718..136086a5e5ba8 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php
@@ -12,37 +12,118 @@
namespace Symfony\Component\Form\Tests\Extension\Validator;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\Extension\Validator\Constraints\Form as FormConstraint;
use Symfony\Component\Form\Extension\Validator\ValidatorExtension;
+use Symfony\Component\Form\Extension\Validator\ValidatorTypeGuesser;
+use Symfony\Component\Form\Form;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\Form\FormFactoryBuilder;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+use Symfony\Component\Validator\Constraints\NotBlank;
+use Symfony\Component\Validator\Mapping\CascadingStrategy;
+use Symfony\Component\Validator\Mapping\ClassMetadata;
+use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory;
+use Symfony\Component\Validator\Mapping\Loader\StaticMethodLoader;
+use Symfony\Component\Validator\Mapping\TraversalStrategy;
+use Symfony\Component\Validator\Tests\Fixtures\FakeMetadataFactory;
+use Symfony\Component\Validator\Validation;
class ValidatorExtensionTest extends TestCase
{
public function test2Dot5ValidationApi()
{
- $validator = $this->getMockBuilder('Symfony\Component\Validator\Validator\RecursiveValidator')
- ->disableOriginalConstructor()
- ->getMock();
- $metadata = $this->getMockBuilder('Symfony\Component\Validator\Mapping\ClassMetadata')
- ->setMethods(['addConstraint', 'addPropertyConstraint'])
- ->disableOriginalConstructor()
- ->getMock();
+ $metadata = new ClassMetadata(Form::class);
- $validator->expects($this->once())
- ->method('getMetadataFor')
- ->with($this->identicalTo('Symfony\Component\Form\Form'))
- ->will($this->returnValue($metadata));
+ $metadataFactory = new FakeMetadataFactory();
+ $metadataFactory->addMetadata($metadata);
- // Verify that the constraints are added
- $metadata->expects($this->once())
- ->method('addConstraint')
- ->with($this->isInstanceOf('Symfony\Component\Form\Extension\Validator\Constraints\Form'));
-
- $metadata->expects($this->once())
- ->method('addPropertyConstraint')
- ->with('children', $this->isInstanceOf('Symfony\Component\Validator\Constraints\Valid'));
+ $validator = Validation::createValidatorBuilder()
+ ->setMetadataFactory($metadataFactory)
+ ->getValidator();
$extension = new ValidatorExtension($validator);
- $guesser = $extension->loadTypeGuesser();
- $this->assertInstanceOf('Symfony\Component\Form\Extension\Validator\ValidatorTypeGuesser', $guesser);
+ $this->assertInstanceOf(ValidatorTypeGuesser::class, $extension->loadTypeGuesser());
+
+ $this->assertCount(1, $metadata->getConstraints());
+ $this->assertInstanceOf(FormConstraint::class, $metadata->getConstraints()[0]);
+
+ $this->assertSame(CascadingStrategy::CASCADE, $metadata->getPropertyMetadata('children')[0]->cascadingStrategy);
+ $this->assertSame(TraversalStrategy::IMPLICIT, $metadata->getPropertyMetadata('children')[0]->traversalStrategy);
+ }
+
+ public function testDataConstraintsInvalidateFormEvenIfFieldIsNotSubmitted()
+ {
+ $form = $this->createForm(FooType::class);
+ $form->submit(['baz' => 'foobar'], false);
+
+ $this->assertTrue($form->isSubmitted());
+ $this->assertFalse($form->isValid());
+ $this->assertFalse($form->get('bar')->isSubmitted());
+ $this->assertCount(1, $form->get('bar')->getErrors());
+ }
+
+ public function testFieldConstraintsDoNotInvalidateFormIfFieldIsNotSubmitted()
+ {
+ $form = $this->createForm(FooType::class);
+ $form->submit(['bar' => 'foobar'], false);
+
+ $this->assertTrue($form->isSubmitted());
+ $this->assertTrue($form->isValid());
+ }
+
+ public function testFieldConstraintsInvalidateFormIfFieldIsSubmitted()
+ {
+ $form = $this->createForm(FooType::class);
+ $form->submit(['bar' => 'foobar', 'baz' => ''], false);
+
+ $this->assertTrue($form->isSubmitted());
+ $this->assertFalse($form->isValid());
+ $this->assertTrue($form->get('bar')->isSubmitted());
+ $this->assertTrue($form->get('bar')->isValid());
+ $this->assertTrue($form->get('baz')->isSubmitted());
+ $this->assertFalse($form->get('baz')->isValid());
+ }
+
+ private function createForm($type)
+ {
+ $validator = Validation::createValidatorBuilder()
+ ->setMetadataFactory(new LazyLoadingMetadataFactory(new StaticMethodLoader()))
+ ->getValidator();
+ $formFactoryBuilder = new FormFactoryBuilder();
+ $formFactoryBuilder->addExtension(new ValidatorExtension($validator));
+ $formFactory = $formFactoryBuilder->getFormFactory();
+
+ return $formFactory->create($type);
+ }
+}
+
+class Foo
+{
+ public $bar;
+ public $baz;
+
+ public static function loadValidatorMetadata(ClassMetadata $metadata)
+ {
+ $metadata->addPropertyConstraint('bar', new NotBlank());
+ }
+}
+
+class FooType extends AbstractType
+{
+ public function buildForm(FormBuilderInterface $builder, array $options)
+ {
+ $builder
+ ->add('bar')
+ ->add('baz', null, [
+ 'constraints' => [new NotBlank()],
+ ])
+ ;
+ }
+
+ public function configureOptions(OptionsResolver $resolver)
+ {
+ $resolver->setDefault('data_class', Foo::class);
}
}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorTypeGuesserTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorTypeGuesserTest.php
index d14c89c2cbda1..878bbfad21bc5 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorTypeGuesserTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorTypeGuesserTest.php
@@ -23,6 +23,8 @@
use Symfony\Component\Validator\Constraints\Range;
use Symfony\Component\Validator\Constraints\Type;
use Symfony\Component\Validator\Mapping\ClassMetadata;
+use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
+use Symfony\Component\Validator\Tests\Fixtures\FakeMetadataFactory;
/**
* @author franek
@@ -45,18 +47,15 @@ class ValidatorTypeGuesserTest extends TestCase
private $metadata;
/**
- * @var \PHPUnit_Framework_MockObject_MockObject
+ * @var MetadataFactoryInterface
*/
private $metadataFactory;
protected function setUp()
{
$this->metadata = new ClassMetadata(self::TEST_CLASS);
- $this->metadataFactory = $this->getMockBuilder('Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface')->getMock();
- $this->metadataFactory->expects($this->any())
- ->method('getMetadataFor')
- ->with(self::TEST_CLASS)
- ->will($this->returnValue($this->metadata));
+ $this->metadataFactory = new FakeMetadataFactory();
+ $this->metadataFactory->addMetadata($this->metadata);
$this->guesser = new ValidatorTypeGuesser($this->metadataFactory);
}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php
index 1278a86e22c2a..2fa3e928926ee 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php
@@ -12,8 +12,11 @@
namespace Symfony\Component\Form\Tests\Extension\Validator\ViolationMapper;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\Exception\TransformationFailedException;
+use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper;
use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapper;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormConfigBuilder;
@@ -34,7 +37,7 @@ class ViolationMapperTest extends TestCase
const LEVEL_2 = 3;
/**
- * @var \PHPUnit_Framework_MockObject_MockObject
+ * @var EventDispatcherInterface
*/
private $dispatcher;
@@ -60,7 +63,7 @@ class ViolationMapperTest extends TestCase
protected function setUp()
{
- $this->dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock();
+ $this->dispatcher = new EventDispatcher();
$this->mapper = new ViolationMapper();
$this->message = 'Message';
$this->messageTemplate = 'Message template';
@@ -76,7 +79,7 @@ protected function getForm($name = 'name', $propertyPath = null, $dataClass = nu
$config->setInheritData($inheritData);
$config->setPropertyPath($propertyPath);
$config->setCompound(true);
- $config->setDataMapper($this->getDataMapper());
+ $config->setDataMapper(new PropertyPathMapper());
if (!$synchronized) {
$config->addViewTransformer(new CallbackTransformer(
@@ -88,14 +91,6 @@ function () { throw new TransformationFailedException(); }
return new Form($config);
}
- /**
- * @return \PHPUnit_Framework_MockObject_MockObject
- */
- private function getDataMapper()
- {
- return $this->getMockBuilder('Symfony\Component\Form\DataMapperInterface')->getMock();
- }
-
/**
* @param $propertyPath
*
@@ -210,7 +205,7 @@ public function testAbortDotRuleMappingIfNotSynchronized()
$this->assertCount(0, $grandChild->getErrors(), $grandChild->getName().' should not have an error, but has one');
}
- public function testAbortMappingIfNotSubmitted()
+ public function testMappingIfNotSubmitted()
{
$violation = $this->getConstraintViolation('children[address].data.street');
$parent = $this->getForm('parent');
@@ -230,10 +225,10 @@ public function testAbortMappingIfNotSubmitted()
$this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one');
$this->assertCount(0, $child->getErrors(), $child->getName().' should not have an error, but has one');
- $this->assertCount(0, $grandChild->getErrors(), $grandChild->getName().' should not have an error, but has one');
+ $this->assertCount(1, $grandChild->getErrors(), $grandChild->getName().' should have one error');
}
- public function testAbortDotRuleMappingIfNotSubmitted()
+ public function testDotRuleMappingIfNotSubmitted()
{
$violation = $this->getConstraintViolation('data.address');
$parent = $this->getForm('parent');
@@ -255,7 +250,7 @@ public function testAbortDotRuleMappingIfNotSubmitted()
$this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one');
$this->assertCount(0, $child->getErrors(), $child->getName().' should not have an error, but has one');
- $this->assertCount(0, $grandChild->getErrors(), $grandChild->getName().' should not have an error, but has one');
+ $this->assertCount(1, $grandChild->getErrors(), $grandChild->getName().' should have one error');
}
public function provideDefaultTests()
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/CustomArrayObject.php b/src/Symfony/Component/Form/Tests/Fixtures/CustomArrayObject.php
index 39758e781884a..006724014a2a1 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/CustomArrayObject.php
+++ b/src/Symfony/Component/Form/Tests/Fixtures/CustomArrayObject.php
@@ -26,7 +26,7 @@ public function __construct(array $array = null)
public function offsetExists($offset)
{
- return array_key_exists($offset, $this->array);
+ return \array_key_exists($offset, $this->array);
}
public function offsetGet($offset)
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/default_option_with_normalizer.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/default_option_with_normalizer.txt
index 391c3e694d974..1824c46a7e5f8 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/default_option_with_normalizer.txt
+++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/default_option_with_normalizer.txt
@@ -15,10 +15,9 @@ Symfony\Component\Form\Extension\Core\Type\ChoiceType (choice_translation_domain
---------------- --------------------%s
Allowed values - %s
---------------- --------------------%s
- Normalizer Closure%s{%w
- parameters: 2 %s
+ Normalizer Closure%s{%A
file: "%s%eExtension%eCore%eType%eChoiceType.php"%w
- line: "%s to %s" %s
+ line: %s
} %s
---------------- --------------------%s
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/overridden_option_with_default_closures.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/overridden_option_with_default_closures.txt
index 846d6f384684c..780a4e5cb3589 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/overridden_option_with_default_closures.txt
+++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/overridden_option_with_default_closures.txt
@@ -8,15 +8,13 @@ Symfony\Component\Form\Tests\Console\Descriptor\FooType (empty_data)
Default Value: null %s
%s
Closure(s): [ %s
- Closure%s{%w
- parameters: 1 %s
+ Closure%s{%A
file: "%s%eExtension%eCore%eType%eFormType.php"%w
- line: "%s to %s" %s
+ line: %s
}, %s
- Closure%s{%w
- parameters: 2 %s
+ Closure%s{%A
file: "%s%eTests%eConsole%eDescriptor%eAbstractDescriptorTest.php"%w
- line: "%s to %s" %s
+ line: %s
} %s
] %s
---------------- ----------------------%s
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/required_option_with_allowed_values.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/required_option_with_allowed_values.txt
index 8cc88a550ab70..ca203a285b2c2 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/required_option_with_allowed_values.txt
+++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/required_option_with_allowed_values.txt
@@ -16,10 +16,9 @@ Symfony\Component\Form\Tests\Console\Descriptor\FooType (foo)
"baz" %s
] %s
---------------- --------------------%s
- Normalizer Closure%s{%w
- parameters: 2 %s
+ Normalizer Closure%s{%A
file: "%s%eTests%eConsole%eDescriptor%eAbstractDescriptorTest.php"%w
- line: "%s to %s" %s
+ line: %s
} %s
---------------- --------------------%s
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/FixedDataTransformer.php b/src/Symfony/Component/Form/Tests/Fixtures/FixedDataTransformer.php
index e768b1d7cfac9..29391160968e2 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/FixedDataTransformer.php
+++ b/src/Symfony/Component/Form/Tests/Fixtures/FixedDataTransformer.php
@@ -25,7 +25,7 @@ public function __construct(array $mapping)
public function transform($value)
{
- if (!array_key_exists($value, $this->mapping)) {
+ if (!\array_key_exists($value, $this->mapping)) {
throw new TransformationFailedException(sprintf('No mapping for value "%s"', $value));
}
diff --git a/src/Symfony/Component/Form/Tests/NativeRequestHandlerTest.php b/src/Symfony/Component/Form/Tests/NativeRequestHandlerTest.php
index 077f477d4a23a..25a4716650755 100644
--- a/src/Symfony/Component/Form/Tests/NativeRequestHandlerTest.php
+++ b/src/Symfony/Component/Form/Tests/NativeRequestHandlerTest.php
@@ -179,6 +179,59 @@ public function testMethodOverrideHeaderIgnoredIfNotPost()
$this->assertFalse($form->isSubmitted());
}
+ public function testFormIgnoresMethodFieldIfRequestMethodIsMatched()
+ {
+ $form = $this->createForm('foo', 'PUT', true);
+ $form->add($this->createForm('bar'));
+
+ $this->setRequestData('PUT', [
+ 'foo' => [
+ '_method' => 'PUT',
+ 'bar' => 'baz',
+ ],
+ ]);
+
+ $this->requestHandler->handleRequest($form, $this->request);
+
+ $this->assertSame([], $form->getExtraData());
+ }
+
+ public function testFormDoesNotIgnoreMethodFieldIfRequestMethodIsNotMatched()
+ {
+ $form = $this->createForm('foo', 'PUT', true);
+ $form->add($this->createForm('bar'));
+
+ $this->setRequestData('PUT', [
+ 'foo' => [
+ '_method' => 'DELETE',
+ 'bar' => 'baz',
+ ],
+ ]);
+
+ $this->requestHandler->handleRequest($form, $this->request);
+
+ $this->assertSame(['_method' => 'DELETE'], $form->getExtraData());
+ }
+
+ public function testMethodSubFormIsSubmitted()
+ {
+ $form = $this->createForm('foo', 'PUT', true);
+ $form->add($this->createForm('_method'));
+ $form->add($this->createForm('bar'));
+
+ $this->setRequestData('PUT', [
+ 'foo' => [
+ '_method' => 'PUT',
+ 'bar' => 'baz',
+ ],
+ ]);
+
+ $this->requestHandler->handleRequest($form, $this->request);
+
+ $this->assertTrue($form->get('_method')->isSubmitted());
+ $this->assertSame('PUT', $form->get('_method')->getData());
+ }
+
protected function setRequestData($method, $data, $files = [])
{
if ('GET' === $method) {
@@ -201,7 +254,7 @@ protected function getRequestHandler()
return new NativeRequestHandler($this->serverParams);
}
- protected function getMockFile($suffix = '')
+ protected function getUploadedFile($suffix = '')
{
return [
'name' => 'upload'.$suffix.'.txt',
diff --git a/src/Symfony/Component/Form/Tests/VersionAwareTest.php b/src/Symfony/Component/Form/Tests/VersionAwareTest.php
new file mode 100644
index 0000000000000..2b8489a6a27cf
--- /dev/null
+++ b/src/Symfony/Component/Form/Tests/VersionAwareTest.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests;
+
+trait VersionAwareTest
+{
+ protected static $supportedFeatureSetVersion = 304;
+
+ /**
+ * @param int $requiredFeatureSetVersion
+ */
+ protected function requiresFeatureSet($requiredFeatureSetVersion)
+ {
+ if ($requiredFeatureSetVersion > static::$supportedFeatureSetVersion) {
+ $this->markTestSkipped(sprintf('Test requires features from symfony/form %.2f but only version %.2f is supported.', $requiredFeatureSetVersion / 100, static::$supportedFeatureSetVersion / 100));
+ }
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/HeaderBag.php b/src/Symfony/Component/HttpFoundation/HeaderBag.php
index 937afe6b7acdd..9798173e649bf 100644
--- a/src/Symfony/Component/HttpFoundation/HeaderBag.php
+++ b/src/Symfony/Component/HttpFoundation/HeaderBag.php
@@ -112,7 +112,7 @@ public function get($key, $default = null, $first = true)
$key = str_replace('_', '-', strtolower($key));
$headers = $this->all();
- if (!array_key_exists($key, $headers)) {
+ if (!\array_key_exists($key, $headers)) {
if (null === $default) {
return $first ? null : [];
}
@@ -168,7 +168,7 @@ public function set($key, $values, $replace = true)
*/
public function has($key)
{
- return array_key_exists(str_replace('_', '-', strtolower($key)), $this->all());
+ return \array_key_exists(str_replace('_', '-', strtolower($key)), $this->all());
}
/**
@@ -245,7 +245,7 @@ public function addCacheControlDirective($key, $value = true)
*/
public function hasCacheControlDirective($key)
{
- return array_key_exists($key, $this->cacheControl);
+ return \array_key_exists($key, $this->cacheControl);
}
/**
@@ -257,7 +257,7 @@ public function hasCacheControlDirective($key)
*/
public function getCacheControlDirective($key)
{
- return array_key_exists($key, $this->cacheControl) ? $this->cacheControl[$key] : null;
+ return \array_key_exists($key, $this->cacheControl) ? $this->cacheControl[$key] : null;
}
/**
diff --git a/src/Symfony/Component/HttpFoundation/ParameterBag.php b/src/Symfony/Component/HttpFoundation/ParameterBag.php
index 3c6ba46a6a08e..f05e4a2154ecb 100644
--- a/src/Symfony/Component/HttpFoundation/ParameterBag.php
+++ b/src/Symfony/Component/HttpFoundation/ParameterBag.php
@@ -81,7 +81,7 @@ public function add(array $parameters = [])
*/
public function get($key, $default = null)
{
- return array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default;
+ return \array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default;
}
/**
@@ -104,7 +104,7 @@ public function set($key, $value)
*/
public function has($key)
{
- return array_key_exists($key, $this->parameters);
+ return \array_key_exists($key, $this->parameters);
}
/**
diff --git a/src/Symfony/Component/HttpFoundation/RedirectResponse.php b/src/Symfony/Component/HttpFoundation/RedirectResponse.php
index 970d82b5a29a7..5e1dfe58507fb 100644
--- a/src/Symfony/Component/HttpFoundation/RedirectResponse.php
+++ b/src/Symfony/Component/HttpFoundation/RedirectResponse.php
@@ -42,7 +42,7 @@ public function __construct($url, $status = 302, $headers = [])
throw new \InvalidArgumentException(sprintf('The HTTP status code is not a redirect ("%s" given).', $status));
}
- if (301 == $status && !array_key_exists('cache-control', $headers)) {
+ if (301 == $status && !\array_key_exists('cache-control', $headers)) {
$this->headers->remove('cache-control');
}
}
diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php
index 8dc01b91ea714..f6ff0f509954d 100644
--- a/src/Symfony/Component/HttpFoundation/Request.php
+++ b/src/Symfony/Component/HttpFoundation/Request.php
@@ -303,10 +303,10 @@ public static function createFromGlobals()
// HTTP_CONTENT_TYPE and HTTP_CONTENT_LENGTH fields.
$server = $_SERVER;
if ('cli-server' === \PHP_SAPI) {
- if (array_key_exists('HTTP_CONTENT_LENGTH', $_SERVER)) {
+ if (\array_key_exists('HTTP_CONTENT_LENGTH', $_SERVER)) {
$server['CONTENT_LENGTH'] = $_SERVER['HTTP_CONTENT_LENGTH'];
}
- if (array_key_exists('HTTP_CONTENT_TYPE', $_SERVER)) {
+ if (\array_key_exists('HTTP_CONTENT_TYPE', $_SERVER)) {
$server['CONTENT_TYPE'] = $_SERVER['HTTP_CONTENT_TYPE'];
}
}
@@ -691,7 +691,7 @@ public static function setTrustedHeaderName($key, $value)
$key = self::HEADER_CLIENT_PROTO;
} elseif ('client_port' === $key) {
$key = self::HEADER_CLIENT_PORT;
- } elseif (!array_key_exists($key, self::$trustedHeaders)) {
+ } elseif (!\array_key_exists($key, self::$trustedHeaders)) {
throw new \InvalidArgumentException(sprintf('Unable to set the trusted header name for key "%s".', $key));
}
@@ -722,7 +722,7 @@ public static function getTrustedHeaderName($key)
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the Request::getTrustedHeaderSet() method instead.', __METHOD__), E_USER_DEPRECATED);
}
- if (!array_key_exists($key, self::$trustedHeaders)) {
+ if (!\array_key_exists($key, self::$trustedHeaders)) {
throw new \InvalidArgumentException(sprintf('Unable to get the trusted header name for key "%s".', $key));
}
diff --git a/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php b/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php
index dc721a9deefab..1dc8dc2c5f651 100644
--- a/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php
+++ b/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php
@@ -160,7 +160,7 @@ public function remove($key)
*/
public function hasCacheControlDirective($key)
{
- return array_key_exists($key, $this->computedCacheControl);
+ return \array_key_exists($key, $this->computedCacheControl);
}
/**
@@ -168,7 +168,7 @@ public function hasCacheControlDirective($key)
*/
public function getCacheControlDirective($key)
{
- return array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null;
+ return \array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null;
}
public function setCookie(Cookie $cookie)
diff --git a/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php b/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php
index b5666f8543e38..07118e891b5de 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php
@@ -63,7 +63,7 @@ public function getStorageKey()
*/
public function has($name)
{
- return array_key_exists($name, $this->attributes);
+ return \array_key_exists($name, $this->attributes);
}
/**
@@ -71,7 +71,7 @@ public function has($name)
*/
public function get($name, $default = null)
{
- return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
+ return \array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
}
/**
@@ -107,7 +107,7 @@ public function replace(array $attributes)
public function remove($name)
{
$retval = null;
- if (array_key_exists($name, $this->attributes)) {
+ if (\array_key_exists($name, $this->attributes)) {
$retval = $this->attributes[$name];
unset($this->attributes[$name]);
}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php b/src/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php
index 95e48253bfb0d..bbf2e39c81059 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php
@@ -44,7 +44,7 @@ public function has($name)
return false;
}
- return array_key_exists($name, $attributes);
+ return \array_key_exists($name, $attributes);
}
/**
@@ -60,7 +60,7 @@ public function get($name, $default = null)
return $default;
}
- return array_key_exists($name, $attributes) ? $attributes[$name] : $default;
+ return \array_key_exists($name, $attributes) ? $attributes[$name] : $default;
}
/**
@@ -81,7 +81,7 @@ public function remove($name)
$retval = null;
$attributes = &$this->resolveAttributePath($name);
$name = $this->resolveKey($name);
- if (null !== $attributes && array_key_exists($name, $attributes)) {
+ if (null !== $attributes && \array_key_exists($name, $attributes)) {
$retval = $attributes[$name];
unset($attributes[$name]);
}
@@ -123,7 +123,7 @@ protected function &resolveAttributePath($name, $writeContext = false)
unset($parts[\count($parts) - 1]);
foreach ($parts as $part) {
- if (null !== $array && !array_key_exists($part, $array)) {
+ if (null !== $array && !\array_key_exists($part, $array)) {
if (!$writeContext) {
$null = null;
diff --git a/src/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php b/src/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php
index 12345c8238c78..451c4a5a1d089 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php
@@ -53,7 +53,7 @@ public function initialize(array &$flashes)
// The logic: messages from the last request will be stored in new, so we move them to previous
// This request we will show what is in 'display'. What is placed into 'new' this time round will
// be moved to display next time round.
- $this->flashes['display'] = array_key_exists('new', $this->flashes) ? $this->flashes['new'] : [];
+ $this->flashes['display'] = \array_key_exists('new', $this->flashes) ? $this->flashes['new'] : [];
$this->flashes['new'] = [];
}
@@ -78,7 +78,7 @@ public function peek($type, array $default = [])
*/
public function peekAll()
{
- return array_key_exists('display', $this->flashes) ? (array) $this->flashes['display'] : [];
+ return \array_key_exists('display', $this->flashes) ? (array) $this->flashes['display'] : [];
}
/**
@@ -132,7 +132,7 @@ public function set($type, $messages)
*/
public function has($type)
{
- return array_key_exists($type, $this->flashes['display']) && $this->flashes['display'][$type];
+ return \array_key_exists($type, $this->flashes['display']) && $this->flashes['display'][$type];
}
/**
diff --git a/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php b/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php
index 19baabb943b6e..f5d984af081b2 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php
@@ -123,7 +123,7 @@ public function setAll(array $messages)
*/
public function has($type)
{
- return array_key_exists($type, $this->flashes) && $this->flashes[$type];
+ return \array_key_exists($type, $this->flashes) && $this->flashes[$type];
}
/**
diff --git a/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php b/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php
index 758e9043da2a3..e657f6143075f 100644
--- a/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php
+++ b/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php
@@ -131,7 +131,7 @@ protected function doGetArguments(Request $request, $controller, array $paramete
$attributes = $request->attributes->all();
$arguments = [];
foreach ($parameters as $param) {
- if (array_key_exists($param->name, $attributes)) {
+ if (\array_key_exists($param->name, $attributes)) {
if ($this->supportsVariadic && $param->isVariadic() && \is_array($attributes[$param->name])) {
$arguments = array_merge($arguments, array_values($attributes[$param->name]));
} else {
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php
index 4a257ff5f8ee6..4346e0ec0f8e1 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php
@@ -40,9 +40,6 @@ abstract class DataCollector implements DataCollectorInterface, \Serializable
*/
private $cloner;
- /**
- * @internal
- */
public function serialize()
{
$trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
@@ -51,9 +48,6 @@ public function serialize()
return $isCalledFromOverridingMethod ? $this->data : serialize($this->data);
}
- /**
- * @internal
- */
public function unserialize($data)
{
$this->data = \is_array($data) ? $data : unserialize($data);
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php
index e25e4fb37d719..4266b22fec9b9 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php
@@ -176,9 +176,6 @@ public function reset()
$this->clonesIndex = 0;
}
- /**
- * @internal
- */
public function serialize()
{
if ($this->clonesCount !== $this->clonesIndex) {
@@ -198,9 +195,6 @@ public function serialize()
return $ser;
}
- /**
- * @internal
- */
public function unserialize($data)
{
$this->data = unserialize($data);
diff --git a/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php b/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php
index 67db029af173f..e7e36d188bdea 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php
@@ -56,13 +56,12 @@ public function onKernelException(GetResponseForExceptionEvent $event)
} catch (\Exception $e) {
$this->logException($e, sprintf('Exception thrown when handling an exception (%s: %s at %s line %s)', \get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()));
- $wrapper = $e;
-
- while ($prev = $wrapper->getPrevious()) {
+ $prev = $e;
+ do {
if ($exception === $wrapper = $prev) {
throw $e;
}
- }
+ } while ($prev = $wrapper->getPrevious());
$prev = new \ReflectionProperty($wrapper instanceof \Exception ? \Exception::class : \Error::class, 'previous');
$prev->setAccessible(true);
diff --git a/src/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.php b/src/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.php
index 73defb9d3ef7e..ce88bd45dea26 100644
--- a/src/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.php
+++ b/src/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.php
@@ -80,7 +80,7 @@ public function render($uri, Request $request, array $options = [])
return SubRequestHandler::handle($this->kernel, $subRequest, HttpKernelInterface::SUB_REQUEST, false);
} catch (\Exception $e) {
// we dispatch the exception event to trigger the logging
- // the response that comes back is simply ignored
+ // the response that comes back is ignored
if (isset($options['ignore_errors']) && $options['ignore_errors'] && $this->dispatcher) {
$event = new GetResponseForExceptionEvent($this->kernel, $request, HttpKernelInterface::SUB_REQUEST, $e);
diff --git a/src/Symfony/Component/HttpKernel/HttpCache/ResponseCacheStrategy.php b/src/Symfony/Component/HttpKernel/HttpCache/ResponseCacheStrategy.php
index 168e9564b0c99..862bac557dd77 100644
--- a/src/Symfony/Component/HttpKernel/HttpCache/ResponseCacheStrategy.php
+++ b/src/Symfony/Component/HttpKernel/HttpCache/ResponseCacheStrategy.php
@@ -5,10 +5,6 @@
*
* (c) Fabien Potencier
*
- * This code is partially based on the Rack-Cache library by Ryan Tomayko,
- * which is released under the MIT license.
- * (based on commit 02d2b48d75bcb63cf1c0c7149c077ad256542801)
- *
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
@@ -28,30 +24,69 @@
*/
class ResponseCacheStrategy implements ResponseCacheStrategyInterface
{
- private $cacheable = true;
+ /**
+ * Cache-Control headers that are sent to the final response if they appear in ANY of the responses.
+ */
+ private static $overrideDirectives = ['private', 'no-cache', 'no-store', 'no-transform', 'must-revalidate', 'proxy-revalidate'];
+
+ /**
+ * Cache-Control headers that are sent to the final response if they appear in ALL of the responses.
+ */
+ private static $inheritDirectives = ['public', 'immutable'];
+
private $embeddedResponses = 0;
- private $ttls = [];
- private $maxAges = [];
private $isNotCacheableResponseEmbedded = false;
+ private $age = 0;
+ private $flagDirectives = [
+ 'no-cache' => null,
+ 'no-store' => null,
+ 'no-transform' => null,
+ 'must-revalidate' => null,
+ 'proxy-revalidate' => null,
+ 'public' => null,
+ 'private' => null,
+ 'immutable' => null,
+ ];
+ private $ageDirectives = [
+ 'max-age' => null,
+ 's-maxage' => null,
+ 'expires' => null,
+ ];
/**
* {@inheritdoc}
*/
public function add(Response $response)
{
- if (!$response->isFresh() || !$response->isCacheable()) {
- $this->cacheable = false;
- } else {
- $maxAge = $response->getMaxAge();
- $this->ttls[] = $response->getTtl();
- $this->maxAges[] = $maxAge;
-
- if (null === $maxAge) {
- $this->isNotCacheableResponseEmbedded = true;
+ ++$this->embeddedResponses;
+
+ foreach (self::$overrideDirectives as $directive) {
+ if ($response->headers->hasCacheControlDirective($directive)) {
+ $this->flagDirectives[$directive] = true;
}
}
- ++$this->embeddedResponses;
+ foreach (self::$inheritDirectives as $directive) {
+ if (false !== $this->flagDirectives[$directive]) {
+ $this->flagDirectives[$directive] = $response->headers->hasCacheControlDirective($directive);
+ }
+ }
+
+ $age = $response->getAge();
+ $this->age = max($this->age, $age);
+
+ if ($this->willMakeFinalResponseUncacheable($response)) {
+ $this->isNotCacheableResponseEmbedded = true;
+
+ return;
+ }
+
+ $this->storeRelativeAgeDirective('max-age', $response->headers->getCacheControlDirective('max-age'), $age);
+ $this->storeRelativeAgeDirective('s-maxage', $response->headers->getCacheControlDirective('s-maxage') ?: $response->headers->getCacheControlDirective('max-age'), $age);
+
+ $expires = $response->getExpires();
+ $expires = null !== $expires ? $expires->format('U') - $response->getDate()->format('U') : null;
+ $this->storeRelativeAgeDirective('expires', $expires >= 0 ? $expires : null, 0);
}
/**
@@ -64,33 +99,124 @@ public function update(Response $response)
return;
}
- // Remove validation related headers in order to avoid browsers using
- // their own cache, because some of the response content comes from
- // at least one embedded response (which likely has a different caching strategy).
- if ($response->isValidateable()) {
- $response->setEtag(null);
- $response->setLastModified(null);
+ // Remove validation related headers of the master response,
+ // because some of the response content comes from at least
+ // one embedded response (which likely has a different caching strategy).
+ $response->setEtag(null);
+ $response->setLastModified(null);
+
+ $this->add($response);
+
+ $response->headers->set('Age', $this->age);
+
+ if ($this->isNotCacheableResponseEmbedded) {
+ $response->setExpires($response->getDate());
+
+ if ($this->flagDirectives['no-store']) {
+ $response->headers->set('Cache-Control', 'no-cache, no-store, must-revalidate');
+ } else {
+ $response->headers->set('Cache-Control', 'no-cache, must-revalidate');
+ }
+
+ return;
+ }
+
+ $flags = array_filter($this->flagDirectives);
+
+ if (isset($flags['must-revalidate'])) {
+ $flags['no-cache'] = true;
}
- if (!$response->isFresh() || !$response->isCacheable()) {
- $this->cacheable = false;
+ $response->headers->set('Cache-Control', implode(', ', array_keys($flags)));
+
+ $maxAge = null;
+ $sMaxage = null;
+
+ if (\is_numeric($this->ageDirectives['max-age'])) {
+ $maxAge = $this->ageDirectives['max-age'] + $this->age;
+ $response->headers->addCacheControlDirective('max-age', $maxAge);
}
- if (!$this->cacheable) {
- $response->headers->set('Cache-Control', 'no-cache, must-revalidate');
+ if (\is_numeric($this->ageDirectives['s-maxage'])) {
+ $sMaxage = $this->ageDirectives['s-maxage'] + $this->age;
- return;
+ if ($maxAge !== $sMaxage) {
+ $response->headers->addCacheControlDirective('s-maxage', $sMaxage);
+ }
+ }
+
+ if (\is_numeric($this->ageDirectives['expires'])) {
+ $date = clone $response->getDate();
+ $date->modify('+'.($this->ageDirectives['expires'] + $this->age).' seconds');
+ $response->setExpires($date);
}
+ }
- $this->ttls[] = $response->getTtl();
- $this->maxAges[] = $response->getMaxAge();
+ /**
+ * RFC2616, Section 13.4.
+ *
+ * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.4
+ *
+ * @return bool
+ */
+ private function willMakeFinalResponseUncacheable(Response $response)
+ {
+ // RFC2616: A response received with a status code of 200, 203, 300, 301 or 410
+ // MAY be stored by a cache […] unless a cache-control directive prohibits caching.
+ if ($response->headers->hasCacheControlDirective('no-cache')
+ || $response->headers->getCacheControlDirective('no-store')
+ ) {
+ return true;
+ }
- if ($this->isNotCacheableResponseEmbedded) {
- $response->headers->removeCacheControlDirective('s-maxage');
- } elseif (null !== $maxAge = min($this->maxAges)) {
- $response->setSharedMaxAge($maxAge);
- $response->headers->set('Age', $maxAge - min($this->ttls));
+ // Last-Modified and Etag headers cannot be merged, they render the response uncacheable
+ // by default (except if the response also has max-age etc.).
+ if (\in_array($response->getStatusCode(), [200, 203, 300, 301, 410])
+ && null === $response->getLastModified()
+ && null === $response->getEtag()
+ ) {
+ return false;
+ }
+
+ // RFC2616: A response received with any other status code (e.g. status codes 302 and 307)
+ // MUST NOT be returned in a reply to a subsequent request unless there are
+ // cache-control directives or another header(s) that explicitly allow it.
+ $cacheControl = ['max-age', 's-maxage', 'must-revalidate', 'proxy-revalidate', 'public', 'private'];
+ foreach ($cacheControl as $key) {
+ if ($response->headers->hasCacheControlDirective($key)) {
+ return false;
+ }
+ }
+
+ if ($response->headers->has('Expires')) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Store lowest max-age/s-maxage/expires for the final response.
+ *
+ * The response might have been stored in cache a while ago. To keep things comparable,
+ * we have to subtract the age so that the value is normalized for an age of 0.
+ *
+ * If the value is lower than the currently stored value, we update the value, to keep a rolling
+ * minimal value of each instruction. If the value is NULL, the directive will not be set on the final response.
+ *
+ * @param string $directive
+ * @param int|null $value
+ * @param int $age
+ */
+ private function storeRelativeAgeDirective($directive, $value, $age)
+ {
+ if (null === $value) {
+ $this->ageDirectives[$directive] = false;
+ }
+
+ if (false !== $this->ageDirectives[$directive]) {
+ $value = $value - $age;
+ $this->ageDirectives[$directive] = null !== $this->ageDirectives[$directive] ? min($this->ageDirectives[$directive], $value) : $value;
}
- $response->setMaxAge(0);
}
}
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index 54bcfd171946f..1a985a4c910b9 100644
--- a/src/Symfony/Component/HttpKernel/Kernel.php
+++ b/src/Symfony/Component/HttpKernel/Kernel.php
@@ -67,11 +67,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
private $requestStackSize = 0;
private $resetServices = false;
- const VERSION = '3.4.22';
- const VERSION_ID = 30422;
+ const VERSION = '3.4.23';
+ const VERSION_ID = 30423;
const MAJOR_VERSION = 3;
const MINOR_VERSION = 4;
- const RELEASE_VERSION = 22;
+ const RELEASE_VERSION = 23;
const EXTRA_VERSION = '';
const END_OF_MAINTENANCE = '11/2020';
diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php
index 6d67a177398c2..22cadf7129528 100644
--- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php
@@ -237,4 +237,233 @@ public function testResponseIsExpirableButNotValidateableWhenMasterResponseCombi
$this->assertSame('60', $masterResponse->headers->getCacheControlDirective('s-maxage'));
$this->assertFalse($masterResponse->isValidateable());
}
+
+ /**
+ * @dataProvider cacheControlMergingProvider
+ */
+ public function testCacheControlMerging(array $expects, array $master, array $surrogates)
+ {
+ $cacheStrategy = new ResponseCacheStrategy();
+ $buildResponse = function ($config) {
+ $response = new Response();
+
+ foreach ($config as $key => $value) {
+ switch ($key) {
+ case 'age':
+ $response->headers->set('Age', $value);
+ break;
+
+ case 'expires':
+ $expires = clone $response->getDate();
+ $expires->modify('+'.$value.' seconds');
+ $response->setExpires($expires);
+ break;
+
+ case 'max-age':
+ $response->setMaxAge($value);
+ break;
+
+ case 's-maxage':
+ $response->setSharedMaxAge($value);
+ break;
+
+ case 'private':
+ $response->setPrivate();
+ break;
+
+ case 'public':
+ $response->setPublic();
+ break;
+
+ default:
+ $response->headers->addCacheControlDirective($key, $value);
+ }
+ }
+
+ return $response;
+ };
+
+ foreach ($surrogates as $config) {
+ $cacheStrategy->add($buildResponse($config));
+ }
+
+ $response = $buildResponse($master);
+ $cacheStrategy->update($response);
+
+ foreach ($expects as $key => $value) {
+ if ('expires' === $key) {
+ $this->assertSame($value, $response->getExpires()->format('U') - $response->getDate()->format('U'));
+ } elseif ('age' === $key) {
+ $this->assertSame($value, $response->getAge());
+ } elseif (true === $value) {
+ $this->assertTrue($response->headers->hasCacheControlDirective($key), sprintf('Cache-Control header must have "%s" flag', $key));
+ } elseif (false === $value) {
+ $this->assertFalse(
+ $response->headers->hasCacheControlDirective($key),
+ sprintf('Cache-Control header must NOT have "%s" flag', $key)
+ );
+ } else {
+ $this->assertSame($value, $response->headers->getCacheControlDirective($key), sprintf('Cache-Control flag "%s" should be "%s"', $key, $value));
+ }
+ }
+ }
+
+ public function cacheControlMergingProvider()
+ {
+ yield 'result is public if all responses are public' => [
+ ['private' => false, 'public' => true],
+ ['public' => true],
+ [
+ ['public' => true],
+ ],
+ ];
+
+ yield 'result is private by default' => [
+ ['private' => true, 'public' => false],
+ ['public' => true],
+ [
+ [],
+ ],
+ ];
+
+ yield 'combines public and private responses' => [
+ ['must-revalidate' => false, 'private' => true, 'public' => false],
+ ['public' => true],
+ [
+ ['private' => true],
+ ],
+ ];
+
+ yield 'inherits no-cache from surrogates' => [
+ ['no-cache' => true, 'public' => false],
+ ['public' => true],
+ [
+ ['no-cache' => true],
+ ],
+ ];
+
+ yield 'inherits no-store from surrogate' => [
+ ['no-store' => true, 'public' => false],
+ ['public' => true],
+ [
+ ['no-store' => true],
+ ],
+ ];
+
+ yield 'resolve to lowest possible max-age' => [
+ ['public' => false, 'private' => true, 's-maxage' => false, 'max-age' => '60'],
+ ['public' => true, 'max-age' => 3600],
+ [
+ ['private' => true, 'max-age' => 60],
+ ],
+ ];
+
+ yield 'resolves multiple max-age' => [
+ ['public' => false, 'private' => true, 's-maxage' => false, 'max-age' => '60'],
+ ['private' => true, 'max-age' => 100],
+ [
+ ['private' => true, 'max-age' => 3600],
+ ['public' => true, 'max-age' => 60, 's-maxage' => 60],
+ ['private' => true, 'max-age' => 60],
+ ],
+ ];
+
+ yield 'merge max-age and s-maxage' => [
+ ['public' => true, 's-maxage' => '60', 'max-age' => null],
+ ['public' => true, 's-maxage' => 3600],
+ [
+ ['public' => true, 'max-age' => 60],
+ ],
+ ];
+
+ yield 'result is private when combining private responses' => [
+ ['no-cache' => false, 'must-revalidate' => false, 'private' => true],
+ ['s-maxage' => 60, 'private' => true],
+ [
+ ['s-maxage' => 60, 'private' => true],
+ ],
+ ];
+
+ yield 'result can have s-maxage and max-age' => [
+ ['public' => true, 'private' => false, 's-maxage' => '60', 'max-age' => '30'],
+ ['s-maxage' => 100, 'max-age' => 2000],
+ [
+ ['s-maxage' => 1000, 'max-age' => 30],
+ ['s-maxage' => 500, 'max-age' => 500],
+ ['s-maxage' => 60, 'max-age' => 1000],
+ ],
+ ];
+
+ yield 'does not set headers without value' => [
+ ['max-age' => null, 's-maxage' => null, 'public' => null],
+ ['private' => true],
+ [
+ ['private' => true],
+ ],
+ ];
+
+ yield 'max-age 0 is sent to the client' => [
+ ['private' => true, 'max-age' => '0'],
+ ['max-age' => 0, 'private' => true],
+ [
+ ['max-age' => 60, 'private' => true],
+ ],
+ ];
+
+ yield 'max-age is relative to age' => [
+ ['max-age' => '240', 'age' => 60],
+ ['max-age' => 180],
+ [
+ ['max-age' => 600, 'age' => 60],
+ ],
+ ];
+
+ yield 'retains lowest age of all responses' => [
+ ['max-age' => '160', 'age' => 60],
+ ['max-age' => 600, 'age' => 60],
+ [
+ ['max-age' => 120, 'age' => 20],
+ ],
+ ];
+
+ yield 'max-age can be less than age, essentially expiring the response' => [
+ ['age' => 120, 'max-age' => '90'],
+ ['max-age' => 90, 'age' => 120],
+ [
+ ['max-age' => 120, 'age' => 60],
+ ],
+ ];
+
+ yield 'max-age is 0 regardless of age' => [
+ ['max-age' => '0'],
+ ['max-age' => 60],
+ [
+ ['max-age' => 0, 'age' => 60],
+ ],
+ ];
+
+ yield 'max-age is not negative' => [
+ ['max-age' => '0'],
+ ['max-age' => 0],
+ [
+ ['max-age' => 0, 'age' => 60],
+ ],
+ ];
+
+ yield 'calculates lowest Expires header' => [
+ ['expires' => 60],
+ ['expires' => 60],
+ [
+ ['expires' => 120],
+ ],
+ ];
+
+ yield 'calculates Expires header relative to age' => [
+ ['expires' => 210, 'age' => 120],
+ ['expires' => 90],
+ [
+ ['expires' => 600, 'age' => '120'],
+ ],
+ ];
+ }
}
diff --git a/src/Symfony/Component/Intl/Data/Generator/LocaleDataGenerator.php b/src/Symfony/Component/Intl/Data/Generator/LocaleDataGenerator.php
index 5e0c0a6a1ba6c..11b1b6b6d82d1 100644
--- a/src/Symfony/Component/Intl/Data/Generator/LocaleDataGenerator.php
+++ b/src/Symfony/Component/Intl/Data/Generator/LocaleDataGenerator.php
@@ -157,7 +157,7 @@ private function generateLocaleName($locale, $displayLocale)
// the name sorting
// $name = $this->langBundle->getLanguageName($displayLocale, $lang, $region);
- // Some languages are simply not translated
+ // Some languages are not translated
// Example: "az" (Azerbaijani) has no translation in "af" (Afrikaans)
if (null === ($name = $this->languageDataProvider->getName($lang, $displayLocale))) {
return;
diff --git a/src/Symfony/Component/Intl/Data/Util/RecursiveArrayAccess.php b/src/Symfony/Component/Intl/Data/Util/RecursiveArrayAccess.php
index c133932c9e6ff..09dd551dafa5e 100644
--- a/src/Symfony/Component/Intl/Data/Util/RecursiveArrayAccess.php
+++ b/src/Symfony/Component/Intl/Data/Util/RecursiveArrayAccess.php
@@ -25,7 +25,7 @@ public static function get($array, array $indices)
foreach ($indices as $index) {
// Use array_key_exists() for arrays, isset() otherwise
if (\is_array($array)) {
- if (array_key_exists($index, $array)) {
+ if (\array_key_exists($index, $array)) {
$array = $array[$index];
continue;
}
diff --git a/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php b/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php
index e0c6c236adc84..9ba821eac8009 100644
--- a/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php
+++ b/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php
@@ -460,7 +460,7 @@ public function getPattern()
*/
public function getSymbol($attr)
{
- return array_key_exists($this->style, self::$enSymbols) && array_key_exists($attr, self::$enSymbols[$this->style]) ? self::$enSymbols[$this->style][$attr] : false;
+ return \array_key_exists($this->style, self::$enSymbols) && \array_key_exists($attr, self::$enSymbols[$this->style]) ? self::$enSymbols[$this->style][$attr] : false;
}
/**
@@ -474,7 +474,7 @@ public function getSymbol($attr)
*/
public function getTextAttribute($attr)
{
- return array_key_exists($this->style, self::$enTextAttributes) && array_key_exists($attr, self::$enTextAttributes[$this->style]) ? self::$enTextAttributes[$this->style][$attr] : false;
+ return \array_key_exists($this->style, self::$enTextAttributes) && \array_key_exists($attr, self::$enTextAttributes[$this->style]) ? self::$enTextAttributes[$this->style][$attr] : false;
}
/**
diff --git a/src/Symfony/Component/OptionsResolver/Debug/OptionsResolverIntrospector.php b/src/Symfony/Component/OptionsResolver/Debug/OptionsResolverIntrospector.php
index 146c18bf28236..bacb70b0b04b3 100644
--- a/src/Symfony/Component/OptionsResolver/Debug/OptionsResolverIntrospector.php
+++ b/src/Symfony/Component/OptionsResolver/Debug/OptionsResolverIntrospector.php
@@ -32,7 +32,7 @@ public function __construct(OptionsResolver $optionsResolver)
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist.', $option));
}
- if (!array_key_exists($option, $this->{$property})) {
+ if (!\array_key_exists($option, $this->{$property})) {
throw new NoConfigurationException($message);
}
diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php
index 82efa15e3e23e..8ed03c2af20a7 100644
--- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php
+++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php
@@ -175,7 +175,7 @@ public function setDefault($option, $value)
// to resolve options with lazy closures, normalizers or validation
// rules, none of which can exist for undefined options
// If the option was resolved before, update the resolved value
- if (!isset($this->defined[$option]) || array_key_exists($option, $this->resolved)) {
+ if (!isset($this->defined[$option]) || \array_key_exists($option, $this->resolved)) {
$this->resolved[$option] = $value;
}
@@ -215,7 +215,7 @@ public function setDefaults(array $defaults)
*/
public function hasDefault($option)
{
- return array_key_exists($option, $this->defaults);
+ return \array_key_exists($option, $this->defaults);
}
/**
@@ -280,7 +280,7 @@ public function getRequiredOptions()
*/
public function isMissing($option)
{
- return isset($this->required[$option]) && !array_key_exists($option, $this->defaults);
+ return isset($this->required[$option]) && !\array_key_exists($option, $this->defaults);
}
/**
@@ -694,12 +694,12 @@ public function offsetGet($option)
}
// Shortcut for resolved options
- if (array_key_exists($option, $this->resolved)) {
+ if (\array_key_exists($option, $this->resolved)) {
return $this->resolved[$option];
}
// Check whether the option is set at all
- if (!array_key_exists($option, $this->defaults)) {
+ if (!\array_key_exists($option, $this->defaults)) {
if (!isset($this->defined[$option])) {
throw new NoSuchOptionException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined))));
}
@@ -908,7 +908,7 @@ public function offsetExists($option)
throw new AccessException('Array access is only supported within closures of lazy options and normalizers.');
}
- return array_key_exists($option, $this->defaults);
+ return \array_key_exists($option, $this->defaults);
}
/**
diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php
index ac2ade04c8d82..83b0f1ce13269 100644
--- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php
+++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php
@@ -260,7 +260,7 @@ private static function throwInvalidArgumentException($message, $trace, $i)
return;
}
- if (isset($trace[$i]['file']) && __FILE__ === $trace[$i]['file'] && array_key_exists(0, $trace[$i]['args'])) {
+ if (isset($trace[$i]['file']) && __FILE__ === $trace[$i]['file'] && \array_key_exists(0, $trace[$i]['args'])) {
$pos = strpos($message, $delim = 'must be of the type ') ?: (strpos($message, $delim = 'must be an instance of ') ?: strpos($message, $delim = 'must implement interface '));
$pos += \strlen($delim);
$type = $trace[$i]['args'][0];
@@ -362,7 +362,7 @@ private function readPropertiesUntil($zval, PropertyPathInterface $propertyPath,
if ($isIndex) {
// Create missing nested arrays on demand
if (($zval[self::VALUE] instanceof \ArrayAccess && !$zval[self::VALUE]->offsetExists($property)) ||
- (\is_array($zval[self::VALUE]) && !isset($zval[self::VALUE][$property]) && !array_key_exists($property, $zval[self::VALUE]))
+ (\is_array($zval[self::VALUE]) && !isset($zval[self::VALUE][$property]) && !\array_key_exists($property, $zval[self::VALUE]))
) {
if (!$ignoreInvalidIndices) {
if (!\is_array($zval[self::VALUE])) {
diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/NonTraversableArrayObject.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/NonTraversableArrayObject.php
index 4852e8d58ff98..4b18e725ae9e4 100644
--- a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/NonTraversableArrayObject.php
+++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/NonTraversableArrayObject.php
@@ -26,7 +26,7 @@ public function __construct(array $array = null)
public function offsetExists($offset)
{
- return array_key_exists($offset, $this->array);
+ return \array_key_exists($offset, $this->array);
}
public function offsetGet($offset)
diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TraversableArrayObject.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TraversableArrayObject.php
index 9803a11f848fc..b075286f4a70e 100644
--- a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TraversableArrayObject.php
+++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TraversableArrayObject.php
@@ -26,7 +26,7 @@ public function __construct(array $array = null)
public function offsetExists($offset)
{
- return array_key_exists($offset, $this->array);
+ return \array_key_exists($offset, $this->array);
}
public function offsetGet($offset)
diff --git a/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php b/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php
index 702de4505ecc3..6f348095e8d68 100644
--- a/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php
+++ b/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php
@@ -100,7 +100,7 @@ private function extract($method, array $arguments)
// Calling rawurlencode escapes special characters not allowed in PSR-6's keys
$key = rawurlencode($method.'.'.$serializedArguments);
- if (array_key_exists($key, $this->arrayCache)) {
+ if (\array_key_exists($key, $this->arrayCache)) {
return $this->arrayCache[$key];
}
diff --git a/src/Symfony/Component/Routing/Generator/UrlGenerator.php b/src/Symfony/Component/Routing/Generator/UrlGenerator.php
index 4409796ed669e..b87f4bb5c45f7 100644
--- a/src/Symfony/Component/Routing/Generator/UrlGenerator.php
+++ b/src/Symfony/Component/Routing/Generator/UrlGenerator.php
@@ -139,9 +139,9 @@ protected function doGenerate($variables, $defaults, $requirements, $tokens, $pa
$message = 'Parameter "{parameter}" for route "{route}" must match "{expected}" ("{given}" given) to generate a corresponding URL.';
foreach ($tokens as $token) {
if ('variable' === $token[0]) {
- if (!$optional || !array_key_exists($token[3], $defaults) || null !== $mergedParams[$token[3]] && (string) $mergedParams[$token[3]] !== (string) $defaults[$token[3]]) {
- // check requirement
- if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#'.(empty($token[4]) ? '' : 'u'), $mergedParams[$token[3]])) {
+ if (!$optional || !\array_key_exists($token[3], $defaults) || null !== $mergedParams[$token[3]] && (string) $mergedParams[$token[3]] !== (string) $defaults[$token[3]]) {
+ // check requirement (while ignoring look-around patterns)
+ if (null !== $this->strictRequirements && !preg_match('#^'.preg_replace('/\(\?(?:=|<=|!|strictRequirements) {
throw new InvalidParameterException(strtr($message, ['{parameter}' => $token[3], '{route}' => $name, '{expected}' => $token[2], '{given}' => $mergedParams[$token[3]]]));
}
@@ -195,7 +195,8 @@ protected function doGenerate($variables, $defaults, $requirements, $tokens, $pa
$routeHost = '';
foreach ($hostTokens as $token) {
if ('variable' === $token[0]) {
- if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#i'.(empty($token[4]) ? '' : 'u'), $mergedParams[$token[3]])) {
+ // check requirement (while ignoring look-around patterns)
+ if (null !== $this->strictRequirements && !preg_match('#^'.preg_replace('/\(\?(?:=|<=|!|strictRequirements) {
throw new InvalidParameterException(strtr($message, ['{parameter}' => $token[3], '{route}' => $name, '{expected}' => $token[2], '{given}' => $mergedParams[$token[3]]]));
}
diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php b/src/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php
index ff7325b807fbb..84ee1892d3b7c 100644
--- a/src/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php
+++ b/src/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php
@@ -120,7 +120,7 @@ protected function setParent(self $parent)
*/
public function hasAttribute($name)
{
- return array_key_exists($name, $this->attributes);
+ return \array_key_exists($name, $this->attributes);
}
/**
diff --git a/src/Symfony/Component/Routing/RequestContext.php b/src/Symfony/Component/Routing/RequestContext.php
index df33ec2d21bbd..8ebad8e2538db 100644
--- a/src/Symfony/Component/Routing/RequestContext.php
+++ b/src/Symfony/Component/Routing/RequestContext.php
@@ -316,7 +316,7 @@ public function getParameter($name)
*/
public function hasParameter($name)
{
- return array_key_exists($name, $this->parameters);
+ return \array_key_exists($name, $this->parameters);
}
/**
diff --git a/src/Symfony/Component/Routing/Route.php b/src/Symfony/Component/Routing/Route.php
index 5cebf094a9cde..759b6f3b6e66e 100644
--- a/src/Symfony/Component/Routing/Route.php
+++ b/src/Symfony/Component/Routing/Route.php
@@ -314,7 +314,7 @@ public function getOption($name)
*/
public function hasOption($name)
{
- return array_key_exists($name, $this->options);
+ return \array_key_exists($name, $this->options);
}
/**
@@ -383,7 +383,7 @@ public function getDefault($name)
*/
public function hasDefault($name)
{
- return array_key_exists($name, $this->defaults);
+ return \array_key_exists($name, $this->defaults);
}
/**
@@ -468,7 +468,7 @@ public function getRequirement($key)
*/
public function hasRequirement($key)
{
- return array_key_exists($key, $this->requirements);
+ return \array_key_exists($key, $this->requirements);
}
/**
diff --git a/src/Symfony/Component/Routing/Router.php b/src/Symfony/Component/Routing/Router.php
index 5fa85a538acc9..27c32e14ae8c6 100644
--- a/src/Symfony/Component/Routing/Router.php
+++ b/src/Symfony/Component/Routing/Router.php
@@ -142,7 +142,7 @@ public function setOptions(array $options)
// check option names and live merge, if errors are encountered Exception will be thrown
$invalid = [];
foreach ($options as $key => $value) {
- if (array_key_exists($key, $this->options)) {
+ if (\array_key_exists($key, $this->options)) {
$this->options[$key] = $value;
} else {
$invalid[] = $key;
@@ -164,7 +164,7 @@ public function setOptions(array $options)
*/
public function setOption($key, $value)
{
- if (!array_key_exists($key, $this->options)) {
+ if (!\array_key_exists($key, $this->options)) {
throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key));
}
@@ -182,7 +182,7 @@ public function setOption($key, $value)
*/
public function getOption($key)
{
- if (!array_key_exists($key, $this->options)) {
+ if (!\array_key_exists($key, $this->options)) {
throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key));
}
diff --git a/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php b/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php
index d92161c05c336..7f64a1f378326 100644
--- a/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php
+++ b/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php
@@ -703,6 +703,23 @@ public function testFragmentsCanBeDefinedAsDefaults()
$this->assertEquals('/app.php/testing#fragment', $url);
}
+ /**
+ * @dataProvider provideLookAroundRequirementsInPath
+ */
+ public function testLookRoundRequirementsInPath($expected, $path, $requirement)
+ {
+ $routes = $this->getRoutes('test', new Route($path, [], ['foo' => $requirement, 'baz' => '.+?']));
+ $this->assertSame($expected, $this->getGenerator($routes)->generate('test', ['foo' => 'a/b', 'baz' => 'c/d/e']));
+ }
+
+ public function provideLookAroundRequirementsInPath()
+ {
+ yield ['/app.php/a/b/b%28ar/c/d/e', '/{foo}/b(ar/{baz}', '.+(?=/b\\(ar/)'];
+ yield ['/app.php/a/b/bar/c/d/e', '/{foo}/bar/{baz}', '.+(?!$)'];
+ yield ['/app.php/bar/a/b/bam/c/d/e', '/bar/{foo}/bam/{baz}', '(?<=/bar/).+'];
+ yield ['/app.php/bar/a/b/bam/c/d/e', '/bar/{foo}/bam/{baz}', '(?attributes);
+ return \array_key_exists($name, $this->attributes);
}
/**
@@ -193,7 +193,7 @@ public function hasAttribute($name)
*/
public function getAttribute($name)
{
- if (!array_key_exists($name, $this->attributes)) {
+ if (!\array_key_exists($name, $this->attributes)) {
throw new \InvalidArgumentException(sprintf('This token has no "%s" attribute.', $name));
}
diff --git a/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php
index 39cca72b7b9bc..82e7cd459f2f0 100644
--- a/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php
+++ b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php
@@ -43,7 +43,7 @@ class AccessDecisionManager implements AccessDecisionManagerInterface
public function __construct($voters = [], $strategy = self::STRATEGY_AFFIRMATIVE, $allowIfAllAbstainDecisions = false, $allowIfEqualGrantedDeniedDecisions = true)
{
$strategyMethod = 'decide'.ucfirst($strategy);
- if (!\is_callable([$this, $strategyMethod])) {
+ if ('' === $strategy || !\is_callable([$this, $strategyMethod])) {
throw new \InvalidArgumentException(sprintf('The strategy "%s" is not supported.', $strategy));
}
diff --git a/src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php b/src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php
index 75ccb4d778675..f79eed1961fd5 100644
--- a/src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php
+++ b/src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php
@@ -33,7 +33,7 @@ public function getEncoder($user)
$encoderKey = null;
if ($user instanceof EncoderAwareInterface && (null !== $encoderName = $user->getEncoderName())) {
- if (!array_key_exists($encoderName, $this->encoders)) {
+ if (!\array_key_exists($encoderName, $this->encoders)) {
throw new \RuntimeException(sprintf('The encoder "%s" was not configured.', $encoderName));
}
diff --git a/src/Symfony/Component/Security/Guard/composer.json b/src/Symfony/Component/Security/Guard/composer.json
index de14c9b5e9daa..5d38065ff63bf 100644
--- a/src/Symfony/Component/Security/Guard/composer.json
+++ b/src/Symfony/Component/Security/Guard/composer.json
@@ -17,7 +17,7 @@
],
"require": {
"php": "^5.5.9|>=7.0.8",
- "symfony/security-core": "~2.8|~3.0|~4.0",
+ "symfony/security-core": "~3.4.22|^4.2.3",
"symfony/security-http": "^3.3.13|~4.0"
},
"require-dev": {
diff --git a/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php
index 63d73d235aa10..99c8fcab4e645 100644
--- a/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php
+++ b/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php
@@ -107,7 +107,7 @@ protected function attemptAuthentication(Request $request)
$password = ParameterBagUtils::getRequestParameterValue($request, $this->options['password_parameter']);
}
- if (!\is_string($username) || (\is_object($username) && !\method_exists($username, '__toString'))) {
+ if (!\is_string($username) && (!\is_object($username) || !\method_exists($username, '__toString'))) {
throw new BadRequestHttpException(sprintf('The key "%s" must be a string, "%s" given.', $this->options['username_parameter'], \gettype($username)));
}
diff --git a/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php b/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php
index 79193ece79b04..f80d2ec2caf47 100644
--- a/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php
+++ b/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php
@@ -83,6 +83,10 @@ public function handle(GetResponseEvent $event)
return;
}
+ if (null === $this->tokenStorage->getToken()) {
+ throw new AuthenticationCredentialsNotFoundException('Could not find original Token object.');
+ }
+
if (self::EXIT_VALUE === $username) {
$this->tokenStorage->setToken($this->attemptExitUser($request));
} else {
@@ -164,7 +168,7 @@ private function attemptSwitchUser(Request $request, $username)
*/
private function attemptExitUser(Request $request)
{
- if (null === ($currentToken = $this->tokenStorage->getToken()) || false === $original = $this->getOriginalToken($currentToken)) {
+ if (false === $original = $this->getOriginalToken($this->tokenStorage->getToken())) {
throw new AuthenticationCredentialsNotFoundException('Could not find original Token object.');
}
diff --git a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php
index aea883a04c78d..2f54156732948 100644
--- a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php
+++ b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php
@@ -85,7 +85,7 @@ protected function attemptAuthentication(Request $request)
$password = ParameterBagUtils::getRequestParameterValue($request, $this->options['password_parameter']);
}
- if (!\is_string($username) || (\is_object($username) && !\method_exists($username, '__toString'))) {
+ if (!\is_string($username) && (!\is_object($username) || !\method_exists($username, '__toString'))) {
throw new BadRequestHttpException(sprintf('The key "%s" must be a string, "%s" given.', $this->options['username_parameter'], \gettype($username)));
}
diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php
index d29665d55eb74..709bc4c862f9e 100644
--- a/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php
+++ b/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php
@@ -267,6 +267,17 @@ public function testSwitchUserWithReplacedToken()
$this->assertSame($replacedToken, $this->tokenStorage->getToken());
}
+ /**
+ * @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException
+ */
+ public function testSwitchtUserThrowsAuthenticationExceptionIfNoCurrentToken()
+ {
+ $this->tokenStorage->setToken(null);
+ $this->request->query->set('_switch_user', 'username');
+ $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager);
+ $listener->handle($this->event);
+ }
+
public function testSwitchUserStateless()
{
$token = new UsernamePasswordToken('username', '', 'key', ['ROLE_FOO']);
diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/UsernamePasswordFormAuthenticationListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/UsernamePasswordFormAuthenticationListenerTest.php
index 198ee6566dfdd..f1536db6d25a6 100644
--- a/src/Symfony/Component/Security/Http/Tests/Firewall/UsernamePasswordFormAuthenticationListenerTest.php
+++ b/src/Symfony/Component/Security/Http/Tests/Firewall/UsernamePasswordFormAuthenticationListenerTest.php
@@ -81,7 +81,7 @@ public function testHandleWhenUsernameLength($username, $ok)
* @expectedException \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
* @expectedExceptionMessage The key "_username" must be a string, "array" given.
*/
- public function testHandleNonStringUsername($postOnly)
+ public function testHandleNonStringUsernameWithArray($postOnly)
{
$request = Request::create('/login_check', 'POST', ['_username' => []]);
$request->setSession($this->getMockBuilder('Symfony\Component\HttpFoundation\Session\SessionInterface')->getMock());
@@ -99,6 +99,79 @@ public function testHandleNonStringUsername($postOnly)
$listener->handle($event);
}
+ /**
+ * @dataProvider postOnlyDataProvider
+ * @expectedException \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
+ * @expectedExceptionMessage The key "_username" must be a string, "integer" given.
+ */
+ public function testHandleNonStringUsernameWithInt($postOnly)
+ {
+ $request = Request::create('/login_check', 'POST', ['_username' => 42]);
+ $request->setSession($this->getMockBuilder('Symfony\Component\HttpFoundation\Session\SessionInterface')->getMock());
+ $listener = new UsernamePasswordFormAuthenticationListener(
+ new TokenStorage(),
+ $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface')->getMock(),
+ new SessionAuthenticationStrategy(SessionAuthenticationStrategy::NONE),
+ $httpUtils = new HttpUtils(),
+ 'foo',
+ new DefaultAuthenticationSuccessHandler($httpUtils),
+ new DefaultAuthenticationFailureHandler($this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(), $httpUtils),
+ ['require_previous_session' => false, 'post_only' => $postOnly]
+ );
+ $event = new GetResponseEvent($this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(), $request, HttpKernelInterface::MASTER_REQUEST);
+ $listener->handle($event);
+ }
+
+ /**
+ * @dataProvider postOnlyDataProvider
+ * @expectedException \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
+ * @expectedExceptionMessage The key "_username" must be a string, "object" given.
+ */
+ public function testHandleNonStringUsernameWithObject($postOnly)
+ {
+ $request = Request::create('/login_check', 'POST', ['_username' => new \stdClass()]);
+ $request->setSession($this->getMockBuilder('Symfony\Component\HttpFoundation\Session\SessionInterface')->getMock());
+ $listener = new UsernamePasswordFormAuthenticationListener(
+ new TokenStorage(),
+ $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface')->getMock(),
+ new SessionAuthenticationStrategy(SessionAuthenticationStrategy::NONE),
+ $httpUtils = new HttpUtils(),
+ 'foo',
+ new DefaultAuthenticationSuccessHandler($httpUtils),
+ new DefaultAuthenticationFailureHandler($this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(), $httpUtils),
+ ['require_previous_session' => false, 'post_only' => $postOnly]
+ );
+ $event = new GetResponseEvent($this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(), $request, HttpKernelInterface::MASTER_REQUEST);
+ $listener->handle($event);
+ }
+
+ /**
+ * @dataProvider postOnlyDataProvider
+ */
+ public function testHandleNonStringUsernameWith__toString($postOnly)
+ {
+ $usernameClass = $this->getMockBuilder(DummyUserClass::class)->getMock();
+ $usernameClass
+ ->expects($this->atLeastOnce())
+ ->method('__toString')
+ ->will($this->returnValue('someUsername'));
+
+ $request = Request::create('/login_check', 'POST', ['_username' => $usernameClass]);
+ $request->setSession($this->getMockBuilder('Symfony\Component\HttpFoundation\Session\SessionInterface')->getMock());
+ $listener = new UsernamePasswordFormAuthenticationListener(
+ new TokenStorage(),
+ $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface')->getMock(),
+ new SessionAuthenticationStrategy(SessionAuthenticationStrategy::NONE),
+ $httpUtils = new HttpUtils(),
+ 'foo',
+ new DefaultAuthenticationSuccessHandler($httpUtils),
+ new DefaultAuthenticationFailureHandler($this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(), $httpUtils),
+ ['require_previous_session' => false, 'post_only' => $postOnly]
+ );
+ $event = new GetResponseEvent($this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(), $request, HttpKernelInterface::MASTER_REQUEST);
+ $listener->handle($event);
+ }
+
public function postOnlyDataProvider()
{
return [
@@ -115,3 +188,11 @@ public function getUsernameForLength()
];
}
}
+
+class DummyUserClass
+{
+ public function __toString()
+ {
+ return '';
+ }
+}
diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
index dd43846820cbc..3f181e4e99a55 100644
--- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
+++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
@@ -343,14 +343,14 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref
$allowed = false === $allowedAttributes || \in_array($paramName, $allowedAttributes);
$ignored = !$this->isAllowedAttribute($class, $paramName, $format, $context);
if (method_exists($constructorParameter, 'isVariadic') && $constructorParameter->isVariadic()) {
- if ($allowed && !$ignored && (isset($data[$key]) || array_key_exists($key, $data))) {
+ if ($allowed && !$ignored && (isset($data[$key]) || \array_key_exists($key, $data))) {
if (!\is_array($data[$paramName])) {
throw new RuntimeException(sprintf('Cannot create an instance of %s from serialized data because the variadic parameter %s can only accept an array.', $class, $constructorParameter->name));
}
$params = array_merge($params, $data[$paramName]);
}
- } elseif ($allowed && !$ignored && (isset($data[$key]) || array_key_exists($key, $data))) {
+ } elseif ($allowed && !$ignored && (isset($data[$key]) || \array_key_exists($key, $data))) {
$parameterData = $data[$key];
if (null === $parameterData && $constructorParameter->allowsNull()) {
$params[] = null;
diff --git a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php
index a4b628ef906cd..a36549c3b225c 100644
--- a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php
+++ b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php
@@ -144,7 +144,7 @@ private function formatDateTimeErrors(array $errors)
private function getTimezone(array $context)
{
- $dateTimeZone = array_key_exists(self::TIMEZONE_KEY, $context) ? $context[self::TIMEZONE_KEY] : $this->timezone;
+ $dateTimeZone = \array_key_exists(self::TIMEZONE_KEY, $context) ? $context[self::TIMEZONE_KEY] : $this->timezone;
if (null === $dateTimeZone) {
return null;
diff --git a/src/Symfony/Component/Templating/TemplateReference.php b/src/Symfony/Component/Templating/TemplateReference.php
index 88f71f95b07dd..195616e5d89c0 100644
--- a/src/Symfony/Component/Templating/TemplateReference.php
+++ b/src/Symfony/Component/Templating/TemplateReference.php
@@ -41,7 +41,7 @@ public function __toString()
*/
public function set($name, $value)
{
- if (array_key_exists($name, $this->parameters)) {
+ if (\array_key_exists($name, $this->parameters)) {
$this->parameters[$name] = $value;
} else {
throw new \InvalidArgumentException(sprintf('The template does not support the "%s" parameter.', $name));
@@ -55,7 +55,7 @@ public function set($name, $value)
*/
public function get($name)
{
- if (array_key_exists($name, $this->parameters)) {
+ if (\array_key_exists($name, $this->parameters)) {
return $this->parameters[$name];
}
diff --git a/src/Symfony/Component/Translation/Dumper/FileDumper.php b/src/Symfony/Component/Translation/Dumper/FileDumper.php
index 20d9e55212524..102f9285842f8 100644
--- a/src/Symfony/Component/Translation/Dumper/FileDumper.php
+++ b/src/Symfony/Component/Translation/Dumper/FileDumper.php
@@ -65,7 +65,7 @@ public function setBackup($backup)
*/
public function dump(MessageCatalogue $messages, $options = [])
{
- if (!array_key_exists('path', $options)) {
+ if (!\array_key_exists('path', $options)) {
throw new InvalidArgumentException('The file dumper needs a path option.');
}
diff --git a/src/Symfony/Component/Translation/Dumper/XliffFileDumper.php b/src/Symfony/Component/Translation/Dumper/XliffFileDumper.php
index a26d94987e888..cd867b0967908 100644
--- a/src/Symfony/Component/Translation/Dumper/XliffFileDumper.php
+++ b/src/Symfony/Component/Translation/Dumper/XliffFileDumper.php
@@ -27,11 +27,11 @@ class XliffFileDumper extends FileDumper
public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = [])
{
$xliffVersion = '1.2';
- if (array_key_exists('xliff_version', $options)) {
+ if (\array_key_exists('xliff_version', $options)) {
$xliffVersion = $options['xliff_version'];
}
- if (array_key_exists('default_locale', $options)) {
+ if (\array_key_exists('default_locale', $options)) {
$defaultLocale = $options['default_locale'];
} else {
$defaultLocale = \Locale::getDefault();
@@ -58,7 +58,7 @@ protected function getExtension()
private function dumpXliff1($defaultLocale, MessageCatalogue $messages, $domain, array $options = [])
{
$toolInfo = ['tool-id' => 'symfony', 'tool-name' => 'Symfony'];
- if (array_key_exists('tool_info', $options)) {
+ if (\array_key_exists('tool_info', $options)) {
$toolInfo = array_merge($toolInfo, $options['tool_info']);
}
@@ -195,6 +195,6 @@ private function dumpXliff2($defaultLocale, MessageCatalogue $messages, $domain,
*/
private function hasMetadataArrayInfo($key, $metadata = null)
{
- return null !== $metadata && array_key_exists($key, $metadata) && ($metadata[$key] instanceof \Traversable || \is_array($metadata[$key]));
+ return null !== $metadata && \array_key_exists($key, $metadata) && ($metadata[$key] instanceof \Traversable || \is_array($metadata[$key]));
}
}
diff --git a/src/Symfony/Component/Translation/Resources/bin/translation-status.php b/src/Symfony/Component/Translation/Resources/bin/translation-status.php
new file mode 100644
index 0000000000000..acc8b4e227222
--- /dev/null
+++ b/src/Symfony/Component/Translation/Resources/bin/translation-status.php
@@ -0,0 +1,195 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+$usageInstructions = << false,
+ // NULL = analyze all locales
+ 'locale_to_analyze' => null,
+ // the reference files all the other translations are compared to
+ 'original_files' => [
+ 'src/Symfony/Component/Form/Resources/translations/validators.en.xlf',
+ 'src/Symfony/Component/Security/Core/Resources/translations/security.en.xlf',
+ 'src/Symfony/Component/Validator/Resources/translations/validators.en.xlf',
+ ],
+];
+
+$argc = $_SERVER['argc'];
+$argv = $_SERVER['argv'];
+
+if ($argc > 3) {
+ echo str_replace('translation-status.php', $argv[0], $usageInstructions);
+ exit(1);
+}
+
+foreach (array_slice($argv, 1) as $argumentOrOption) {
+ if (0 === strpos($argumentOrOption, '-')) {
+ $config['verbose_output'] = true;
+ } else {
+ $config['locale_to_analyze'] = $argumentOrOption;
+ }
+}
+
+foreach ($config['original_files'] as $originalFilePath) {
+ if (!file_exists($originalFilePath)) {
+ echo sprintf('The following file does not exist. Make sure that you execute this command at the root dir of the Symfony code repository.%s %s', PHP_EOL, $originalFilePath);
+ exit(1);
+ }
+}
+
+$totalMissingTranslations = 0;
+
+foreach ($config['original_files'] as $originalFilePath) {
+ $translationFilePaths = findTranslationFiles($originalFilePath, $config['locale_to_analyze']);
+ $translationStatus = calculateTranslationStatus($originalFilePath, $translationFilePaths);
+
+ $totalMissingTranslations += array_sum(array_map(function ($translation) {
+ return \count($translation['missingKeys']);
+ }, array_values($translationStatus)));
+
+ printTranslationStatus($originalFilePath, $translationStatus, $config['verbose_output']);
+}
+
+exit($totalMissingTranslations > 0 ? 1 : 0);
+
+function findTranslationFiles($originalFilePath, $localeToAnalyze)
+{
+ $translations = [];
+
+ $translationsDir = dirname($originalFilePath);
+ $originalFileName = basename($originalFilePath);
+ $translationFileNamePattern = str_replace('.en.', '.*.', $originalFileName);
+
+ $translationFiles = glob($translationsDir.'/'.$translationFileNamePattern);
+ foreach ($translationFiles as $filePath) {
+ $locale = extractLocaleFromFilePath($filePath);
+
+ if (null !== $localeToAnalyze && $locale !== $localeToAnalyze) {
+ continue;
+ }
+
+ $translations[$locale] = $filePath;
+ }
+
+ return $translations;
+}
+
+function calculateTranslationStatus($originalFilePath, $translationFilePaths)
+{
+ $translationStatus = [];
+ $allTranslationKeys = extractTranslationKeys($originalFilePath);
+
+ foreach ($translationFilePaths as $locale => $translationPath) {
+ $translatedKeys = extractTranslationKeys($translationPath);
+ $missingKeys = array_diff_key($allTranslationKeys, $translatedKeys);
+
+ $translationStatus[$locale] = [
+ 'total' => \count($allTranslationKeys),
+ 'translated' => \count($translatedKeys),
+ 'missingKeys' => $missingKeys,
+ ];
+ }
+
+ return $translationStatus;
+}
+
+function printTranslationStatus($originalFilePath, $translationStatus, $verboseOutput)
+{
+ printTitle($originalFilePath);
+ printTable($translationStatus, $verboseOutput);
+ echo PHP_EOL.PHP_EOL;
+}
+
+function extractLocaleFromFilePath($filePath)
+{
+ $parts = explode('.', $filePath);
+
+ return $parts[count($parts) - 2];
+}
+
+function extractTranslationKeys($filePath)
+{
+ $translationKeys = [];
+ $contents = new \SimpleXMLElement(file_get_contents($filePath));
+
+ foreach ($contents->file->body->{'trans-unit'} as $translationKey) {
+ $translationId = (string) $translationKey['id'];
+ $translationKey = (string) $translationKey->source;
+
+ $translationKeys[$translationId] = $translationKey;
+ }
+
+ return $translationKeys;
+}
+
+function printTitle($title)
+{
+ echo $title.PHP_EOL;
+ echo str_repeat('=', strlen($title)).PHP_EOL.PHP_EOL;
+}
+
+function printTable($translations, $verboseOutput)
+{
+ $longestLocaleNameLength = max(array_map('strlen', array_keys($translations)));
+
+ foreach ($translations as $locale => $translation) {
+ $isTranslationCompleted = $translation['translated'] === $translation['total'];
+ if ($isTranslationCompleted) {
+ textColorGreen();
+ }
+
+ echo sprintf('| Locale: %-'.$longestLocaleNameLength.'s | Translated: %d/%d', $locale, $translation['translated'], $translation['total']).PHP_EOL;
+
+ textColorNormal();
+
+ if (true === $verboseOutput && \count($translation['missingKeys']) > 0) {
+ echo str_repeat('-', 80).PHP_EOL;
+ echo '| Missing Translations:'.PHP_EOL;
+
+ foreach ($translation['missingKeys'] as $id => $content) {
+ echo sprintf('| (id=%s) %s', $id, $content).PHP_EOL;
+ }
+
+ echo str_repeat('-', 80).PHP_EOL;
+ }
+ }
+}
+
+function textColorGreen()
+{
+ echo "\033[32m";
+}
+
+function textColorNormal()
+{
+ echo "\033[0m";
+}
diff --git a/src/Symfony/Component/Validator/Constraint.php b/src/Symfony/Component/Validator/Constraint.php
index ae27ba21635ee..b1a5e0c1a2b7e 100644
--- a/src/Symfony/Component/Validator/Constraint.php
+++ b/src/Symfony/Component/Validator/Constraint.php
@@ -122,7 +122,7 @@ public function __construct($options = null)
}
if ($options && \is_array($options) && \is_string(key($options))) {
foreach ($options as $option => $value) {
- if (array_key_exists($option, $knownOptions)) {
+ if (\array_key_exists($option, $knownOptions)) {
$this->$option = $value;
unset($missingOptions[$option]);
} else {
@@ -136,7 +136,7 @@ public function __construct($options = null)
throw new ConstraintDefinitionException(sprintf('No default option is configured for constraint %s', \get_class($this)));
}
- if (array_key_exists($option, $knownOptions)) {
+ if (\array_key_exists($option, $knownOptions)) {
$this->$option = $options;
unset($missingOptions[$option]);
} else {
diff --git a/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php b/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php
index fec0080fe02e9..3c95c097e8e9a 100644
--- a/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php
+++ b/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php
@@ -13,7 +13,7 @@
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
use Symfony\Component\PropertyAccess\PropertyAccess;
-use Symfony\Component\PropertyAccess\PropertyAccessor;
+use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
@@ -29,7 +29,7 @@ abstract class AbstractComparisonValidator extends ConstraintValidator
{
private $propertyAccessor;
- public function __construct(PropertyAccessor $propertyAccessor = null)
+ public function __construct(PropertyAccessorInterface $propertyAccessor = null)
{
$this->propertyAccessor = $propertyAccessor;
}
diff --git a/src/Symfony/Component/Validator/Constraints/CollectionValidator.php b/src/Symfony/Component/Validator/Constraints/CollectionValidator.php
index e3a297329d1c8..3b67e40a5c3d4 100644
--- a/src/Symfony/Component/Validator/Constraints/CollectionValidator.php
+++ b/src/Symfony/Component/Validator/Constraints/CollectionValidator.php
@@ -50,7 +50,7 @@ public function validate($value, Constraint $constraint)
foreach ($constraint->fields as $field => $fieldConstraint) {
// bug fix issue #2779
- $existsInArray = \is_array($value) && array_key_exists($field, $value);
+ $existsInArray = \is_array($value) && \array_key_exists($field, $value);
$existsInArrayAccess = $value instanceof \ArrayAccess && $value->offsetExists($field);
if ($existsInArray || $existsInArrayAccess) {
diff --git a/src/Symfony/Component/Validator/Constraints/IbanValidator.php b/src/Symfony/Component/Validator/Constraints/IbanValidator.php
index fc14eea7dbe47..3dcedb47dc1ea 100644
--- a/src/Symfony/Component/Validator/Constraints/IbanValidator.php
+++ b/src/Symfony/Component/Validator/Constraints/IbanValidator.php
@@ -181,7 +181,7 @@ public function validate($value, Constraint $constraint)
}
// ...have a format available
- if (!array_key_exists($countryCode, self::$formats)) {
+ if (!\array_key_exists($countryCode, self::$formats)) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Iban::NOT_SUPPORTED_COUNTRY_CODE_ERROR)
diff --git a/src/Symfony/Component/Validator/Constraints/LessThanOrEqual.php b/src/Symfony/Component/Validator/Constraints/LessThanOrEqual.php
index 74f7aeb8fadea..806ca4990fa02 100644
--- a/src/Symfony/Component/Validator/Constraints/LessThanOrEqual.php
+++ b/src/Symfony/Component/Validator/Constraints/LessThanOrEqual.php
@@ -20,7 +20,7 @@
*/
class LessThanOrEqual extends AbstractComparison
{
- const TOO_HIGH_ERROR = '079d7420-2d13-460c-8756-de810eeb37d2';
+ const TOO_HIGH_ERROR = '30fbb013-d015-4232-8b3b-8f3be97a7e14';
protected static $errorNames = [
self::TOO_HIGH_ERROR => 'TOO_HIGH_ERROR',
diff --git a/src/Symfony/Component/Validator/Constraints/Regex.php b/src/Symfony/Component/Validator/Constraints/Regex.php
index a71e4de2590e6..dc9e5ea8859d9 100644
--- a/src/Symfony/Component/Validator/Constraints/Regex.php
+++ b/src/Symfony/Component/Validator/Constraints/Regex.php
@@ -79,7 +79,7 @@ public function getHtmlPattern()
// Unescape the delimiter
$pattern = str_replace('\\'.$delimiter, $delimiter, substr($this->pattern, 1, -1));
- // If the pattern is inverted, we can simply wrap it in
+ // If the pattern is inverted, we can wrap it in
// ((?!pattern).)*
if (!$this->match) {
return '((?!'.$pattern.').)*';
diff --git a/src/Symfony/Component/Validator/Constraints/Traverse.php b/src/Symfony/Component/Validator/Constraints/Traverse.php
index 4572c9b2193c4..78d115fdbbc74 100644
--- a/src/Symfony/Component/Validator/Constraints/Traverse.php
+++ b/src/Symfony/Component/Validator/Constraints/Traverse.php
@@ -25,7 +25,7 @@ class Traverse extends Constraint
public function __construct($options = null)
{
- if (\is_array($options) && array_key_exists('groups', $options)) {
+ if (\is_array($options) && \array_key_exists('groups', $options)) {
throw new ConstraintDefinitionException(sprintf('The option "groups" is not supported by the constraint %s', __CLASS__));
}
diff --git a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php
index 210d55ea42cea..03b0ae6d8f575 100644
--- a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php
+++ b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php
@@ -371,7 +371,7 @@ public function mergeConstraints(self $source)
*/
public function hasPropertyMetadata($property)
{
- return array_key_exists($property, $this->members);
+ return \array_key_exists($property, $this->members);
}
/**
diff --git a/src/Symfony/Component/Validator/Mapping/ClassMetadataInterface.php b/src/Symfony/Component/Validator/Mapping/ClassMetadataInterface.php
index e48e241ab8d5f..1b6f07ac8eed8 100644
--- a/src/Symfony/Component/Validator/Mapping/ClassMetadataInterface.php
+++ b/src/Symfony/Component/Validator/Mapping/ClassMetadataInterface.php
@@ -86,8 +86,8 @@ public function hasPropertyMetadata($property);
/**
* Returns all metadata instances for the given named property.
*
- * If your implementation does not support properties, simply throw an
- * exception in this method (for example a BadMethodCallException).
+ * If your implementation does not support properties, throw an exception
+ * in this method (for example a BadMethodCallException).
*
* @param string $property The property name
*
diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.af.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.af.xlf
index 177bb0038f523..731e64aa7f2a4 100644
--- a/src/Symfony/Component/Validator/Resources/translations/validators.af.xlf
+++ b/src/Symfony/Component/Validator/Resources/translations/validators.af.xlf
@@ -222,6 +222,118 @@
Unsupported card type or invalid card number.Nie-ondersteunde tipe kaart of ongeldige kredietkaart nommer.
+
+ This is not a valid International Bank Account Number (IBAN).
+ Hierdie is nie 'n geldige Internationale Bank Rekening Nommer (IBAN) nie.
+
+
+ This value is not a valid ISBN-10.
+ Hierdie waarde is nie 'n geldige ISBN-10 nie.
+
+
+ This value is not a valid ISBN-13.
+ Hierdie waarde is nie 'n geldige ISBN-13 nie.
+
+
+ This value is neither a valid ISBN-10 nor a valid ISBN-13.
+ Hierdie waarde is nie 'n geldige ISBN-10 of ISBN-13 nie.
+
+
+ This value is not a valid ISSN.
+ Hierdie waarde is nie 'n geldige ISSN nie.
+
+
+ This value is not a valid currency.
+ Hierdie waarde is nie 'n geldige geldeenheid nie.
+
+
+ This value should be equal to {{ compared_value }}.
+ Hierdie waarde moet gelyk aan {{ compared_value }} wees.
+
+
+ This value should be greater than {{ compared_value }}.
+ Hierdie waarde moet meer as {{ compared_value }} wees.
+
+
+ This value should be greater than or equal to {{ compared_value }}.
+ Hierdie waarde moet meer of gelyk aan {{ compared_value }} wees.
+
+
+ This value should be identical to {{ compared_value_type }} {{ compared_value }}.
+ Hierdie waarde moet identies aan {{ compared_value_type }} {{ compared_value }} wees.
+
+
+ This value should be less than {{ compared_value }}.
+ Hierdie waarde moet minder as {{ compared_value }} wees.
+
+
+ This value should be less than or equal to {{ compared_value }}.
+ Hierdie waarde moet minder of gelyk aan {{ compared_value }} wees.
+
+
+ This value should not be equal to {{ compared_value }}.
+ Hierdie waarde moet nie dieselfde as {{ compared_value }} wees nie.
+
+
+ This value should not be identical to {{ compared_value_type }} {{ compared_value }}.
+ Hierdie waarde moet nie identies as {{ compared_value_type }} {{ compared_value }} wees nie.
+
+
+ The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}.
+ Die beeld aspek is te groot ({{ ratio }}). Die maksimum toegelate aspek is {{ max_ratio }}.
+
+
+ The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}.
+ Die beeld aspek is te klein ({{ ratio }}). Die minimum toegelate aspek is {{ min_ratio }}.
+
+
+ The image is square ({{ width }}x{{ height }}px). Square images are not allowed.
+ Die beeld is vierkantig ({{ width }}x{{ height }}px). Vierkantige beelde word nie toegelaat nie.
+
+
+ The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed.
+ Die beeld is landskap georiënteerd ({{ width }}x{{ height }}px). Landskap georiënteerde beelde word nie toegelaat nie.
+
+
+ The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed.
+ Die beeld dis portret georiënteerd ({{ width }}x{{ height }}px). Portret georiënteerde beelde word nie toegelaat nie.
+
+
+ An empty file is not allowed.
+ 'n Leë lêer word nie toegelaat nie.
+
+
+ The host could not be resolved.
+ Die gasheer kon nie opgelos word nie.
+
+
+ This value does not match the expected {{ charset }} charset.
+ Die waarde stem nie ooreen met die verwagte {{ charset }} karakterstel nie.
+
+
+ This is not a valid Business Identifier Code (BIC).
+ Hierdie is nie 'n geldige Besigheids Identifikasie Kode (BIC) nie.
+
+
+ Error
+ Fout
+
+
+ This is not a valid UUID.
+ Hierdie is nie 'n geldige UUID nie.
+
+
+ This value should be a multiple of {{ compared_value }}.
+ Hierdie waarde moet 'n veelvoud van {{ compared_value }} wees.
+
+
+ This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.
+ Hierdie Besigheids Identifikasie Kode (BIK) is nie geassosieer met IBAN {{ iban }} nie.
+
+
+ This value should be valid JSON.
+ Hierdie waarde moet geldige JSON wees.
+
This value should be false.
- Vrednost treba da bude netačna.
+ Vrednost bi trebalo da bude netačna.This value should be true.
- Vrednost treba da bude tačna.
+ Vrednost bi trebalo da bude tačna.This value should be of type {{ type }}.
- Vrednost treba da bude tipa {{ type }}.
+ Vrednost bi trebalo da bude tipa {{ type }}.This value should be blank.
- Vrednost treba da bude prazna.
+ Vrednost bi trebalo da bude prazna.The value you selected is not a valid choice.
- Vrednost treba da bude jedna od ponuđenih.
+ Odabrana vrednost nije validan izbor.You must select at least {{ limit }} choice.|You must select at least {{ limit }} choices.
- Izaberite bar {{ limit }} mogućnost.|Izaberite bar {{ limit }} mogućnosti.|Izaberite bar {{ limit }} mogućnosti.
+ Morate odabrati bar {{ limit }} mogućnost.|Morate odabrati bar {{ limit }} mogućnosti.You must select at most {{ limit }} choice.|You must select at most {{ limit }} choices.
- Izaberite najviše {{ limit }} mogućnost.|Izaberite najviše {{ limit }} mogućnosti.|Izaberite najviše {{ limit }} mogućnosti.
+ Morate odabrati najviše {{ limit }} mogućnost.|Morate odabrati najviše {{ limit }} mogućnosti.One or more of the given values is invalid.
- Jedna ili više vrednosti je nevalidna.
+ Jedna ili više vrednosti nisu validne.This field was not expected.
- Ovo polje ne očekuje.
+ Ovo polje nije bilo očekivano.This field is missing.
@@ -48,7 +48,7 @@
This value is not a valid datetime.
- Vrednost nije validan datum-vreme.
+ Vrednost nije validno vreme.This value is not a valid email address.
@@ -68,39 +68,39 @@
The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }}.
- Mime tip datoteke nije validan ({{ type }}). Dozvoljeni mime tipovi su {{ types }}.
+ MIME tip datoteke nije validan ({{ type }}). Dozvoljeni MIME tipovi su {{ types }}.This value should be {{ limit }} or less.
- Vrednost treba da bude {{ limit }} ili manje.
+ Vrednost bi trebalo da bude {{ limit }} ili manje.This value is too long. It should have {{ limit }} character or less.|This value is too long. It should have {{ limit }} characters or less.
- Vrednost je predugačka. Treba da ima {{ limit }} karakter ili manje.|Vrednost je predugačka. Treba da ima {{ limit }} karaktera ili manje.|Vrednost je predugačka. Treba da ima {{ limit }} karaktera ili manje.
+ Vrednost je predugačka. Trebalo bi da ima {{ limit }} karakter ili manje.|Vrednost je predugačka. Trebalo bi da ima {{ limit }} karaktera ili manje.This value should be {{ limit }} or more.
- Vrednost treba da bude {{ limit }} ili više.
+ Vrednost bi trebalo da bude {{ limit }} ili više.This value is too short. It should have {{ limit }} character or more.|This value is too short. It should have {{ limit }} characters or more.
- Vrednost je prekratka. Treba da ima {{ limit }} karakter ili više.|Vrednost je prekratka. Treba da ima {{ limit }} karaktera ili više.|Vrednost je prekratka. Treba da ima {{ limit }} karaktera ili više.
+ Vrednost je prekratka. Trebalo bi da ima {{ limit }} karakter ili više.|Vrednost je prekratka. Trebalo bi da ima {{ limit }} karaktera ili više.This value should not be blank.
- Vrednost ne treba da bude prazna.
+ Vrednost ne bi trebalo da bude prazna.This value should not be null.
- Vrednost ne treba da bude null.
+ Vrednost ne bi trebalo da bude prazna.This value should be null.
- Vrednost treba da bude null.
+ Vrednost bi trebalo da bude prazna.This value is not valid.
- Vrednost je nevalidna.
+ Vrednost nije validna.This value is not a valid time.
@@ -112,7 +112,7 @@
The two values should be equal.
- Obe vrednosti treba da budu jednake.
+ Obe vrednosti bi trebalo da budu jednake.The file is too large. Allowed maximum size is {{ limit }} {{ suffix }}.
@@ -128,7 +128,7 @@
This value should be a valid number.
- Vrednost treba da bude validan broj.
+ Vrednost bi trebalo da bude validan broj.This file is not a valid image.
@@ -144,11 +144,11 @@
This value is not a valid locale.
- Vrednost nije validan lokal.
+ Vrednost nije validna međunarodna oznaka jezika.This value is not a valid country.
- Vrednost nije validna zemlja.
+ Vrednost nije validna država.This value is already used.
@@ -160,27 +160,27 @@
The image width is too big ({{ width }}px). Allowed maximum width is {{ max_width }}px.
- Širina slike je prevelika ({{ width }}px). Najeća dozvoljena širina je {{ max_width }}px.
+ Širina slike je prevelika ({{ width }} piksela). Najveća dozvoljena širina je {{ max_width }} piksela.The image width is too small ({{ width }}px). Minimum width expected is {{ min_width }}px.
- Širina slike je premala ({{ width }}px). Najmanja dozvoljena širina je {{ min_width }}px.
+ Širina slike je premala ({{ width }} piksela). Najmanja dozvoljena širina je {{ min_width }} piksela.The image height is too big ({{ height }}px). Allowed maximum height is {{ max_height }}px.
- Visina slike je prevelika ({{ height }}px). Najeća dozvoljena visina je {{ max_height }}px.
+ Visina slike je prevelika ({{ height }} piksela). Najveća dozvoljena visina je {{ max_height }} piksela.The image height is too small ({{ height }}px). Minimum height expected is {{ min_height }}px.
- Visina slike je premala ({{ height }}px). Najmanja dozvoljena visina je {{ min_height }}px.
+ Visina slike je premala ({{ height }} piksela). Najmanja dozvoljena visina je {{ min_height }} piksela.This value should be the user's current password.
- Vrednost treba da bude trenutna korisnička lozinka.
+ Vrednost bi trebalo da bude trenutna korisnička lozinka.This value should have exactly {{ limit }} character.|This value should have exactly {{ limit }} characters.
- Vrednost treba da ima tačno {{ limit }} karakter.|Vrednost treba da ima tačno {{ limit }} karaktera.|Vrednost treba da ima tačno {{ limit }} karaktera.
+ Vrednost bi trebalo da ima tačno {{ limit }} karakter.|Vrednost bi trebalo da ima tačno {{ limit }} karaktera.The file was only partially uploaded.
@@ -204,23 +204,23 @@
This collection should contain {{ limit }} element or more.|This collection should contain {{ limit }} elements or more.
- Ova kolekcija treba da sadrži {{ limit }} ili više elemenata.|Ova kolekcija treba da sadrži {{ limit }} ili više elemenata.|Ova kolekcija treba da sadrži {{ limit }} ili više elemenata.
+ Ova kolekcija bi trebalo da sadrži {{ limit }} ili više elemenata.|Ova kolekcija bi trebalo da sadrži {{ limit }} ili više elemenata.This collection should contain {{ limit }} element or less.|This collection should contain {{ limit }} elements or less.
- Ova kolekcija treba da sadrži {{ limit }} ili manje elemenata.|Ova kolekcija treba da sadrži {{ limit }} ili manje elemenata.|Ova kolekcija treba da sadrži {{ limit }} ili manje elemenata.
+ Ova kolekcija bi trebalo da sadrži {{ limit }} ili manje elemenata.|Ova kolekcija bi trebalo da sadrži {{ limit }} ili manje elemenata.This collection should contain exactly {{ limit }} element.|This collection should contain exactly {{ limit }} elements.
- Ova kolekcija treba da sadrži tačno {{ limit }} element.|Ova kolekcija treba da sadrži tačno {{ limit }} elementa.|Ova kolekcija treba da sadrži tačno {{ limit }} elemenata.
+ Ova kolekcija bi trebalo da sadrži tačno {{ limit }} element.|Ova kolekcija bi trebalo da sadrži tačno {{ limit }} elemenata.Invalid card number.
- Nevalidan broj kartice.
+ Broj kartice nije validan.Unsupported card type or invalid card number.
- Nevalidan broj kartice ili tip kartice nije podržan.
+ Tip kartije nije podržan ili broj kartice nije validan.This is not a valid International Bank Account Number (IBAN).
@@ -248,35 +248,35 @@
This value should be equal to {{ compared_value }}.
- Ova vrednost treba da bude {{ compared_value }}.
+ Ova vrednost bi trebalo da bude jednaka {{ compared_value }}.This value should be greater than {{ compared_value }}.
- Ova vrednost treba da bude veća od {{ compared_value }}.
+ Ova vrednost bi trebalo da bude veća od {{ compared_value }}.This value should be greater than or equal to {{ compared_value }}.
- Ova vrednost treba da bude veća ili jednaka {{ compared_value }}.
+ Ova vrednost bi trebalo da bude veća ili jednaka {{ compared_value }}.This value should be identical to {{ compared_value_type }} {{ compared_value }}.
- Ova vrednost treba da bude identična sa {{ compared_value_type }} {{ compared_value }}.
+ Ova vrednost bi trebalo da bude identična sa {{ compared_value_type }} {{ compared_value }}.This value should be less than {{ compared_value }}.
- Ova vrednost treba da bude manja od {{ compared_value }}.
+ Ova vrednost bi trebalo da bude manja od {{ compared_value }}.This value should be less than or equal to {{ compared_value }}.
- Ova vrednost treba da bude manja ili jednaka {{ compared_value }}.
+ Ova vrednost bi trebalo da bude manja ili jednaka {{ compared_value }}.This value should not be equal to {{ compared_value }}.
- Ova vrednost ne treba da bude jednaka {{ compared_value }}.
+ Ova vrednost ne bi trebalo da bude jednaka {{ compared_value }}.This value should not be identical to {{ compared_value_type }} {{ compared_value }}.
- Ova vrednost ne treba da bude identična sa {{ compared_value_type }} {{ compared_value }}.
+ Ova vrednost ne bi trebalo da bude identična sa {{ compared_value_type }} {{ compared_value }}.The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}.
@@ -288,15 +288,51 @@
The image is square ({{ width }}x{{ height }}px). Square images are not allowed.
- Slika je kvadratna ({{ width }}x{{ height }}px). Kvadratne slike nisu dozvoljene.
+ Slika je kvadratna ({{ width }}x{{ height }} piksela). Kvadratne slike nisu dozvoljene.The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed.
- Slika je orijentacije pejzaža ({{ width }}x{{ height }}px). Pejzažna orijentacija slika nije dozvoljena.
+ Slika je pejzažno orijentisana ({{ width }}x{{ height }} piksela). Pejzažna orijentisane slike nisu dozvoljene.The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed.
- Slika je orijantacije portreta ({{ width }}x{{ height }}px). Portretna orijentacija slika nije dozvoljena.
+ Slika je portretno orijentisana ({{ width }}x{{ height }} piksela). Portretno orijentisane slike nisu dozvoljene.
+
+
+ An empty file is not allowed.
+ Prazna datoteka nije dozvoljena.
+
+
+ The host could not be resolved.
+ Nije moguće odrediti poslužitelja.
+
+
+ This value does not match the expected {{ charset }} charset.
+ Vrednost se ne poklapa sa očekivanim {{ charset }} setom karaktera.
+
+
+ This is not a valid Business Identifier Code (BIC).
+ Ovo nije validan BIC.
+
+
+ Error
+ Greška
+
+
+ This is not a valid UUID.
+ Ovo nije validan univerzalni unikatni identifikator (UUID).
+
+
+ This value should be a multiple of {{ compared_value }}.
+ Ova vrednost bi trebalo da bude višestruko veća od {{ compared_value }}.
+
+
+ This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.
+ BIC kod nije povezan sa IBAN {{ iban }}.
+
+
+ This value should be valid JSON.
+ Ova vrednost bi trebalo da bude validan JSON.