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/.travis.yml b/.travis.yml
index 0cc2e3acbb3af..b279eac0215a8 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,7 +13,6 @@ addons:
- slapd
- zookeeperd
- libzookeeper-mt-dev
- - libsasl2-dev
env:
global:
@@ -50,6 +49,7 @@ before_install:
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 6B05F25D762E3157
sudo add-apt-repository -y ppa:ondrej/php
sudo apt update
+ sudo apt install -y librabbitmq-dev libsodium-dev
- |
# Start Redis cluster
@@ -153,8 +153,6 @@ before_install:
phpenv global $PHP
INI=~/.phpenv/versions/$PHP/etc/conf.d/travis.ini
if ! php --ri sodium > /dev/null; then
- # install libsodium
- sudo apt-get install libsodium-dev -y
tfold ext.libsodium tpecl libsodium sodium.so $INI
fi
@@ -162,23 +160,7 @@ before_install:
tfold ext.mongodb tpecl mongodb-1.6.0alpha1 mongodb.so $INI
tfold ext.igbinary tpecl igbinary-2.0.8 igbinary.so $INI
tfold ext.zookeeper tpecl zookeeper-0.6.0 zookeeper.so $INI
-
- if [[ $PHP != 7.3 ]]; then
- wget http://ftp.debian.org/debian/pool/main/libr/librabbitmq/librabbitmq-dev_0.5.2-2_amd64.deb
- wget http://ftp.debian.org/debian/pool/main/libr/librabbitmq/librabbitmq1_0.5.2-2_amd64.deb
- sudo dpkg -i librabbitmq1_0.5.2-2_amd64.deb librabbitmq-dev_0.5.2-2_amd64.deb
- tfold ext.amqp tpecl amqp-1.9.3 amqp.so $INI
- else
- sudo apt install -y librabbitmq-dev
- git clone https://github.com/pdezwart/php-amqp.git
- cd php-amqp
- phpize
- ./configure
- make
- sudo make install
- cd -
- echo extension = amqp.so >> $INI
- fi
+ tfold ext.amqp tpecl amqp-1.9.4 amqp.so $INI
done
- |
diff --git a/CHANGELOG-4.2.md b/CHANGELOG-4.2.md
index 96994ff62024a..bf1520b310051 100644
--- a/CHANGELOG-4.2.md
+++ b/CHANGELOG-4.2.md
@@ -7,6 +7,51 @@ in 4.2 minor versions.
To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash
To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v4.2.0...v4.2.1
+* 4.2.4 (2019-03-03)
+
+ * bug #30383 [WebProfilerBundle] toolbar: invisible route name in Firefox (inmarelibero)
+ * 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 #30331 [Cache] fix warming up cache.system and apcu (nicolas-grekas)
+ * 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 #30294 [FrameworkBundle] Fix Descriptor throwing on non existent parent (GuilhemN)
+ * 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 #30273 [Validator] Added missing use statement for UnexpectedTypeException (devrck)
+ * 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 #30245 fix lost namespace in eval (fizzka)
+ * 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 #30093 [DependencyInjection] add $lazyLoad context to the generated code for lazy non-shared service by PhpDumper (XuruDragon)
+ * bug #30102 [Workflow] Graphviz dumper escape not always a string (Korbeil)
+ * bug #29884 [Form] CsrfValidationListener marks the token as invalid if it is not a string (umpirsky)
+ * bug #30058 [Routing] fix perf issue when dumping large number of routes (nicolas-grekas)
+ * bug #30062 [Form] do not overwrite the constraint being evaluated (xabbuh)
+ * bug #30074 Fix wrong value in file id attribute for Xliff 2.0 (deguif)
+ * bug #30078 [Messenger] Fix DataCollector template (ottaviano)
+ * bug #30087 [PhpUnitBridge] fix PHP 5.3 compat (nicolas-grekas)
+
* 4.2.3 (2019-02-03)
* bug #30050 [Cache] fix pruning pdo cache for vendors that throw on execute (bendavies)
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 877cf0c796408..b6b40057db1df 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -10,15 +10,15 @@ Symfony is the result of the work of many people who made the code better
- Christian Flothmann (xabbuh)
- Tobias Schultze (tobion)
- Christophe Coevoet (stof)
+ - Robin Chalas (chalas_r)
- Jordi Boggiano (seldaek)
- Victor Berchet (victor)
- - Robin Chalas (chalas_r)
- Kévin Dunglas (dunglas)
- Maxime Steinhausser (ogizanagi)
- Jakub Zalas (jakubzalas)
- Johannes S (johannes)
- - Kris Wallsmith (kriswallsmith)
- Ryan Weaver (weaverryan)
+ - Kris Wallsmith (kriswallsmith)
- Javier Eguiluz (javier.eguiluz)
- Roland Franssen (ro0)
- Grégoire Pineau (lyrixx)
@@ -61,14 +61,14 @@ Symfony is the result of the work of many people who made the code better
- Kevin Bond (kbond)
- Pierre du Plessis (pierredup)
- Henrik Bjørnskov (henrikbjorn)
+ - Alexander M. Turek (derrabus)
- Miha Vrhovnik
- Diego Saint Esteben (dii3g0)
- - Alexander M. Turek (derrabus)
- Konstantin Kudryashov (everzet)
- Bilal Amarni (bamarni)
+ - Mathieu Piot (mpiot)
- Florin Patan (florinpatan)
- Gábor Egyed (1ed)
- - Mathieu Piot (mpiot)
- Titouan Galopin (tgalopin)
- Vladimir Reznichenko (kalessil)
- Michel Weimerskirch (mweimerskirch)
@@ -82,11 +82,11 @@ Symfony is the result of the work of many people who made the code better
- Issei Murasawa (issei_m)
- Arnout Boks (aboks)
- Deni
+ - Grégoire Paris (greg0ire)
- Henrik Westphal (snc)
- Dariusz Górecki (canni)
- - Douglas Greenshields (shieldo)
- - Grégoire Paris (greg0ire)
- Valentin Udaltsov (vudaltsov)
+ - Douglas Greenshields (shieldo)
- Dariusz Ruminski
- Lee McDermott
- Brandon Turner
@@ -109,12 +109,13 @@ Symfony is the result of the work of many people who made the code better
- Brice BERNARD (brikou)
- Baptiste Clavié (talus)
- marc.weistroff
+ - Tomáš Votruba (tomas_votruba)
- David Buchmann (dbu)
- lenar
- Alexander Schwenn (xelaris)
- Włodzimierz Gajda (gajdaw)
- Chris Wilkinson (thewilkybarkid)
- - Tomáš Votruba (tomas_votruba)
+ - Jérôme Vasseur (jvasseur)
- Peter Kokot (maastermedia)
- Jacob Dreesen (jdreesen)
- Florian Voutzinos (florianv)
@@ -124,7 +125,6 @@ Symfony is the result of the work of many people who made the code better
- Daniel Wehner (dawehner)
- excelwebzone
- Gordon Franke (gimler)
- - Jérôme Vasseur (jvasseur)
- Javier Spagnoletti (phansys)
- Fabien Pennequin (fabienpennequin)
- Eric GELOEN (gelo)
@@ -144,6 +144,7 @@ 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)
@@ -175,6 +176,7 @@ Symfony is the result of the work of many people who made the code better
- Amal Raghav (kertz)
- Jonathan Ingram (jonathaningram)
- Artur Kotyrba
+ - Tyson Andre
- GDIBass
- SpacePossum
- jeremyFreeAgent (Jérémy Romey) (jeremyfreeagent)
@@ -182,7 +184,6 @@ Symfony is the result of the work of many people who made the code better
- Matthieu Napoli (mnapoli)
- Florent Mata (fmata)
- Warnar Boekkooi (boekkooi)
- - Thomas Calvet (fancyweb)
- Dmitrii Chekaliuk (lazyhammer)
- Clément JOBEILI (dator)
- Daniel Espendiller
@@ -198,6 +199,7 @@ Symfony is the result of the work of many people who made the code better
- Mathieu Lemoine (lemoinem)
- Christian Schmidt
- Andreas Hucks (meandmymonkey)
+ - Tom Van Looy (tvlooy)
- Noel Guilbert (noel)
- Yanick Witschi (toflar)
- Marek Štípek (maryo)
@@ -205,6 +207,7 @@ Symfony is the result of the work of many people who made the code better
- bronze1man
- sun (sun)
- Larry Garfield (crell)
+ - George Mponos (gmponos)
- Michaël Perrin (michael.perrin)
- Nikolay Labinskiy (e-moe)
- Martin Schuhfuß (usefulthink)
@@ -227,17 +230,16 @@ Symfony is the result of the work of many people who made the code better
- Justin Hileman (bobthecow)
- Blanchon Vincent (blanchonvincent)
- Michele Orselli (orso)
- - Tom Van Looy (tvlooy)
- Sven Paulus (subsven)
- Rui Marinho (ruimarinho)
- Eugene Wissner
- Pascal Montoya
- - George Mponos (gmponos)
- Julien Brochet (mewt)
- Leo Feyer
- Tristan Darricau (nicofuma)
- Marcel Beerta (mazen)
- Pavel Batanov (scaytrase)
+ - Samuel NELA (snela)
- Loïc Faugeron
- Hidde Wieringa (hiddewie)
- Marco Pivetta (ocramius)
@@ -282,7 +284,6 @@ Symfony is the result of the work of many people who made the code better
- GordonsLondon
- Jan Sorgalla (jsor)
- Ray
- - Tyson Andre
- Chekote
- Thomas Adam
- Viktor Bocharskyi (bocharsky_bw)
@@ -290,7 +291,6 @@ Symfony is the result of the work of many people who made the code better
- Diego Agulló (aeoris)
- Andreas Schempp (aschempp)
- jdhoek
- - Samuel NELA (snela)
- Massimiliano Arione (garak)
- Bob den Otter (bopp)
- Frank de Jonge (frenkynet)
@@ -301,6 +301,7 @@ Symfony is the result of the work of many people who made the code better
- Colin O'Dell (colinodell)
- Giorgio Premi
- Jan Schädlich (jschaedl)
+ - Ben Davies (bendavies)
- Beau Simensen (simensen)
- Michael Hirschler (mvhirsch)
- Robert Kiss (kepten)
@@ -313,6 +314,7 @@ Symfony is the result of the work of many people who made the code better
- Peter Kruithof (pkruithof)
- Michael Holm (hollo)
- Remon van de Kamp (rpkamp)
+ - Mathieu Lechat
- Marc Weistroff (futurecat)
- Christian Schmidt
- MatTheCat
@@ -356,7 +358,6 @@ Symfony is the result of the work of many people who made the code better
- Ricard Clau (ricardclau)
- Mark Challoner (markchalloner)
- Gennady Telegin (gtelegin)
- - Ben Davies (bendavies)
- Erin Millard
- Artur Melo (restless)
- Matthew Lewinski (lewinski)
@@ -379,11 +380,11 @@ Symfony is the result of the work of many people who made the code better
- Felix Labrecque
- Yaroslav Kiliba
- Terje Bråten
- - Mathieu Lechat
- Robbert Klarenbeek (robbertkl)
- JhonnyL
- David Badura (davidbadura)
- hossein zolfi (ocean)
+ - Alexander Schranz (alexander-schranz)
- Clément Gautier (clementgautier)
- Sanpi
- Eduardo Gulias (egulias)
@@ -483,7 +484,6 @@ Symfony is the result of the work of many people who made the code better
- Marek Pietrzak
- Luc Vieillescazes (iamluc)
- franek (franek)
- - Alexander Schranz (alexander-schranz)
- Christian Wahler
- Gintautas Miselis
- Rob Bast
@@ -606,6 +606,7 @@ Symfony is the result of the work of many people who made the code better
- Sebastian Blum
- aubx
- Marvin Butkereit
+ - Renan
- Ricky Su (ricky)
- Gildas Quéméner (gquemener)
- Charles-Henri Bruyand
@@ -675,6 +676,7 @@ Symfony is the result of the work of many people who made the code better
- Michael Devery (mickadoo)
- Antoine Corcy
- Sascha Grossenbacher
+ - Emanuele Panzeri (thepanz)
- Szijarto Tamas
- Robin Lehrmann (robinlehrmann)
- Catalin Dan
@@ -713,6 +715,7 @@ Symfony is the result of the work of many people who made the code better
- Mark Sonnabaum
- Massimiliano Braglia (massimilianobraglia)
- Richard Quadling
+ - Raphaëll Roussel
- jochenvdv
- Arturas Smorgun (asarturas)
- Alexander Volochnev (exelenz)
@@ -731,6 +734,7 @@ 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
@@ -738,6 +742,7 @@ Symfony is the result of the work of many people who made the code better
- Sergey Yastrebov
- Trent Steel (trsteel88)
- Yuen-Chi Lian
+ - Tarjei Huse (tarjei)
- Besnik Br
- Jose Gonzalez
- Oleksii Zhurbytskyi
@@ -764,6 +769,7 @@ Symfony is the result of the work of many people who made the code better
- John Bohn (jbohn)
- Marc Morera (mmoreram)
- Saif Eddin Gmati (azjezz)
+ - Stadly
- Andrew Hilobok (hilobok)
- Noah Heck (myesain)
- Christian Soronellas (theunic)
@@ -853,6 +859,7 @@ Symfony is the result of the work of many people who made the code better
- Jelle Kapitein
- Benoît Bourgeois
- mantulo
+ - Stefan Kruppa
- corphi
- grizlik
- Derek ROTH
@@ -885,6 +892,7 @@ Symfony is the result of the work of many people who made the code better
- Forfarle (forfarle)
- Harry Walter (haswalt)
- Johnson Page (jwpage)
+ - Michael Käfer (michael_kaefer)
- Ruben Gonzalez (rubenruateltek)
- Michael Roterman (wtfzdotnet)
- Arno Geurts
@@ -950,6 +958,7 @@ Symfony is the result of the work of many people who made the code better
- Franz Wilding (killerpoke)
- ProgMiner
- Oleg Golovakhin (doc_tr)
+ - Joost van Driel
- Icode4Food (icode4food)
- Radosław Benkel
- kevin.nadin
@@ -996,6 +1005,7 @@ Symfony is the result of the work of many people who made the code better
- Wojciech Sznapka
- Gavin Staniforth
- Ariel J. Birnbaum
+ - Pablo Borowicz
- Mathieu Santostefano
- Arjan Keeman
- Máximo Cuadros (mcuadros)
@@ -1031,6 +1041,7 @@ Symfony is the result of the work of many people who made the code better
- Sergey Novikov (s12v)
- Marcos Quesada (marcos_quesada)
- Matthew Vickery (mattvick)
+ - MARYNICH Mikhail (mmarynich-ext)
- Viktor Novikov (panzer_commander)
- Paul Mitchum (paul-m)
- Angel Koilov (po_taka)
@@ -1085,6 +1096,7 @@ Symfony is the result of the work of many people who made the code better
- Andrew Tch
- Alexander Cheprasov
- Rodrigo Díez Villamuera (rodrigodiez)
+ - James Hudson
- e-ivanov
- Einenlum
- Jochen Bayer (jocl)
@@ -1095,6 +1107,7 @@ Symfony is the result of the work of many people who made the code better
- wizhippo
- Mathias STRASSER (roukmoute)
- Thomason, James
+ - Gordienko Vladislav
- Viacheslav Sychov
- Alexandre Quercia (alquerci)
- Helmut Hummel (helhum)
@@ -1157,7 +1170,6 @@ Symfony is the result of the work of many people who made the code better
- Vacheslav Silyutin
- Juan Traverso
- Alain Flaus (halundra)
- - Tarjei Huse (tarjei)
- tsufeki
- Philipp Strube
- Clement Herreman (clemherreman)
@@ -1240,6 +1252,7 @@ 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)
@@ -1290,7 +1303,6 @@ Symfony is the result of the work of many people who made the code better
- WedgeSama
- Felds Liscia
- Chihiro Adachi (chihiro-adachi)
- - Emanuele Panzeri (thepanz)
- Raphaëll Roussel
- Tadcka
- Beth Binkovitz
@@ -1404,6 +1416,7 @@ Symfony is the result of the work of many people who made the code better
- Gavin Staniforth
- Alessandro Tagliapietra (alex88)
- Biji (biji)
+ - Alex Teterin (errogaht)
- Gunnar Lium (gunnarlium)
- Tiago Garcia (tiagojsag)
- Artiom
@@ -1446,6 +1459,7 @@ Symfony is the result of the work of many people who made the code better
- Paul Seiffert (seiffert)
- Vasily Khayrulin (sirian)
- Stefan Koopmanschap (skoop)
+ - Stas Soroka (stasyan)
- Stefan Hüsges (tronsha)
- Jake Bishop (yakobeyak)
- Dan Blows
@@ -1546,6 +1560,7 @@ Symfony is the result of the work of many people who made the code better
- Muhammed Akbulut
- Aaron Somi
- Michał Dąbrowski (defrag)
+ - Konstantin Grachev (grachevko)
- Simone Fumagalli (hpatoio)
- Brian Graham (incognito)
- Kevin Vergauwen (innocenzo)
@@ -1635,6 +1650,7 @@ Symfony is the result of the work of many people who made the code better
- Darryl Hein (xmmedia)
- Sadicov Vladimir (xtech)
- Kevin EMO (zarcox)
+ - Andrzej
- Alexander Zogheb
- Rémi Blaise
- Nicolas Séverin
@@ -1869,6 +1885,7 @@ Symfony is the result of the work of many people who made the code better
- Şəhriyar İmanov
- Alexis BOYER
- Kaipi Yann
+ - adam-mospan
- Sam Williams
- Guillaume Aveline
- Adrian Philipp
@@ -1906,7 +1923,6 @@ Symfony is the result of the work of many people who made the code better
- Shude
- Ondřej Führer
- Sema
- - Michael Käfer
- Elan Ruusamäe
- Thorsten Hallwas
- Michael Squires
@@ -2074,9 +2090,9 @@ Symfony is the result of the work of many people who made the code better
- drublic
- Andreas Streichardt
- Pascal Hofmann
- - Stefan Kruppa
- smokeybear87
- Gustavo Adrian
+ - damaya
- Kevin Weber
- Ben Scott
- Dionysis Arvanitis
diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md
index 23ed6fbd33534..8947744a9f180 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 5c33f71f0446d..5b1d78fbf82c8 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 d9f7f391d2f2c..49dfd9bfbce6e 100644
--- a/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php
+++ b/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php
@@ -165,7 +165,7 @@ protected function getMetadata($class)
// normalize class name
$class = self::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 769e9bb20e02f..20f68399571f9 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/Monolog/Handler/FingersCrossed/HttpCodeActivationStrategy.php b/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/HttpCodeActivationStrategy.php
index b31817d76d515..ae8fd3650a36b 100644
--- a/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/HttpCodeActivationStrategy.php
+++ b/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/HttpCodeActivationStrategy.php
@@ -31,10 +31,10 @@ class HttpCodeActivationStrategy extends ErrorLevelActivationStrategy
public function __construct(RequestStack $requestStack, array $exclusions, $actionLevel)
{
foreach ($exclusions as $exclusion) {
- if (!array_key_exists('code', $exclusion)) {
+ if (!\array_key_exists('code', $exclusion)) {
throw new \LogicException(sprintf('An exclusion must have a "code" key'));
}
- if (!array_key_exists('urls', $exclusion)) {
+ if (!\array_key_exists('urls', $exclusion)) {
throw new \LogicException(sprintf('An exclusion must have a "urls" key'));
}
}
diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php
index d18e161dd9079..3b71124eb8e2e 100644
--- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php
+++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php
@@ -98,15 +98,15 @@ public static function register($mode = 0)
'legacyCount' => 0,
'otherCount' => 0,
'remaining vendorCount' => 0,
- 'unsilenced' => [],
- 'remaining' => [],
- 'legacy' => [],
- 'other' => [],
- 'remaining vendor' => [],
+ 'unsilenced' => array(),
+ 'remaining' => array(),
+ 'legacy' => array(),
+ 'other' => array(),
+ 'remaining vendor' => array(),
];
- $deprecationHandler = function ($type, $msg, $file, $line, $context = []) use (&$deprecations, $getMode, $UtilPrefix, $inVendors) {
+ $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) {
$ErrorHandler = $UtilPrefix.'ErrorHandler';
return $ErrorHandler::handleError($type, $msg, $file, $line, $context);
diff --git a/src/Symfony/Bridge/PhpUnit/composer.json b/src/Symfony/Bridge/PhpUnit/composer.json
index 4d72603eb452f..c629e8719b5ab 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 c9611e3761654..800d86c20ef0f 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 7d063614e7a29..3bf3d5ed41359 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');
@@ -1900,6 +1902,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 330b8e70fa7fc..f8a8e45e8ff59 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 0b712562a98c7..ed8db234ad8c9 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 6bb2ab6d43abf..f974f08580360 100644
--- a/src/Symfony/Bridge/Twig/Tests/Node/TransNodeTest.php
+++ b/src/Symfony/Bridge/Twig/Tests/Node/TransNodeTest.php
@@ -50,7 +50,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');
}
return sprintf('($context["%s"] ?? $this->getContext($context, "%1$s"))', $name);
diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json
index ec231764f8012..24034230f8f49 100644
--- a/src/Symfony/Bridge/Twig/composer.json
+++ b/src/Symfony/Bridge/Twig/composer.json
@@ -24,7 +24,7 @@
"symfony/asset": "~3.4|~4.0",
"symfony/dependency-injection": "~3.4|~4.0",
"symfony/finder": "~3.4|~4.0",
- "symfony/form": "^4.2.3",
+ "symfony/form": "^4.2.4",
"symfony/http-foundation": "~3.4|~4.0",
"symfony/http-kernel": "~3.4|~4.0",
"symfony/polyfill-intl-icu": "~1.0",
@@ -43,7 +43,7 @@
},
"conflict": {
"symfony/console": "<3.4",
- "symfony/form": "<4.2.3",
+ "symfony/form": "<4.2.4",
"symfony/translation": "<4.2"
},
"suggest": {
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php
index a4a10805f677a..7d3373f3ebbf8 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php
@@ -138,7 +138,7 @@ private function getConfigForPath(array $config, string $path, string $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 d2afe921ff104..484ca4fefd8c2 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php
@@ -11,6 +11,7 @@
namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor;
+use Symfony\Component\Config\Resource\ClassExistenceResource;
use Symfony\Component\Console\Descriptor\DescriptorInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\DependencyInjection\Alias;
@@ -226,7 +227,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);
}
@@ -292,6 +293,11 @@ public static function getClassDescription(string $class, string &$resolvedClass
{
$resolvedClass = $class;
try {
+ $resource = new ClassExistenceResource($class, false);
+
+ // isFresh() will explode ONLY if a parent class/trait does not exist
+ $resource->isFresh(0);
+
$r = new \ReflectionClass($class);
$resolvedClass = $r->name;
diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php
index 06d57dcdb24da..44fae86bc05a7 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 89180c03ca5b1..56e11b07f6aba 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php
@@ -274,7 +274,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 79566a1ad97a8..c0ec5e412e616 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 d66a081aabaa0..3e66b05d0ba81 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php
@@ -112,7 +112,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 4f5e1a1dcc316..5a2aebc2eda42 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
@@ -327,7 +327,7 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode)
}
foreach ($places as $name => $place) {
- if (\is_array($place) && array_key_exists('name', $place)) {
+ if (\is_array($place) && \array_key_exists('name', $place)) {
continue;
}
$place['name'] = $name;
@@ -365,7 +365,7 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode)
}
foreach ($transitions as $name => $transition) {
- if (\is_array($transition) && array_key_exists('name', $transition)) {
+ if (\is_array($transition) && \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 87ce95e44bc13..b786923b8e045 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -919,7 +919,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 {
@@ -1117,7 +1117,7 @@ private function registerValidationConfiguration(array $config, ContainerBuilder
$definition = $container->findDefinition('validator.email');
$definition->replaceArgument(0, $config['email_validation_mode']);
- 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.');
}
@@ -1125,7 +1125,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 999f4aed36f96..166122c03cf78 100644
--- a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
+++ b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
@@ -95,7 +95,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 f2b57a31e72c6..7de149d7e51ce 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
@@ -35,7 +35,6 @@
-
@@ -76,10 +75,6 @@
-
-
-
-
@@ -90,18 +85,13 @@
-
-
-
-
-
-
+
@@ -114,13 +104,14 @@
+
-
+
@@ -238,7 +229,6 @@
-
@@ -258,6 +248,7 @@
+
@@ -352,6 +343,14 @@
+
+
+
+
+
+
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelShutdownOnTearDownTrait.php b/src/Symfony/Bundle/FrameworkBundle/Test/KernelShutdownOnTearDownTrait.php
new file mode 100644
index 0000000000000..217c2ae4576be
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Test/KernelShutdownOnTearDownTrait.php
@@ -0,0 +1,43 @@
+
+ *
+ * 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()) {
+ /**
+ * @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 fa562f608858e..666831b041590 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;
/**
@@ -112,7 +114,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()
{
@@ -125,12 +127,4 @@ protected static function ensureKernelShutdown()
}
static::$container = null;
}
-
- /**
- * 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 e91ecc50caca2..9e4c46d585557 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php
@@ -51,7 +51,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 93028bf8c2278..ee13386a4b767 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php
@@ -22,7 +22,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 b00b6a10e5337..acdaea9a8cbed 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 8b1bf4cbdd81a..dc145f1e63038 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 108afc2f7cea4..a3d41a70a6a15 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
@@ -67,7 +67,7 @@ public function prepend(ContainerBuilder $container)
foreach ($container->getExtensionConfig('framework') as $config) {
if (isset($config['session']) && \is_array($config['session'])) {
$rememberMeSecureDefault = $config['session']['cookie_secure'] ?? $rememberMeSecureDefault;
- $rememberMeSameSiteDefault = array_key_exists('cookie_samesite', $config['session']) ? $config['session']['cookie_samesite'] : $rememberMeSameSiteDefault;
+ $rememberMeSameSiteDefault = \array_key_exists('cookie_samesite', $config['session']) ? $config['session']['cookie_samesite'] : $rememberMeSameSiteDefault;
}
}
foreach ($this->listenerPositions as $position) {
@@ -412,7 +412,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/WebProfilerBundle/Resources/views/Collector/messenger.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig
index ed1183738098c..f3e8fecece384 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig
+++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig
@@ -30,15 +30,15 @@
{% endblock %}
{% block menu %}
-
- {{ include('@WebProfiler/Icon/messenger.svg') }}
- Messages
- {% if collector.exceptionsCount > 0 %}
-
- {{ collector.exceptionsCount }}
-
- {% endif %}
-
+
+ {{ include('@WebProfiler/Icon/messenger.svg') }}
+ Messages
+ {% if collector.exceptionsCount > 0 %}
+
+ {{ collector.exceptionsCount }}
+
+ {% endif %}
+
{% endblock %}
{% block head %}
@@ -71,31 +71,31 @@
No messages have been collected.
{% else %}
-
-
-
- {% set messages = collector.messages %}
- {% set exceptionsCount = collector.exceptionsCount %}
-
All{{ messages|length }}
-
-
-
Ordered list of dispatched messages across all your buses
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig
index 9c1cad235047f..0230c84fb90d1 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig
+++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig
@@ -103,6 +103,7 @@
.sf-toolbar-block > a:hover {
display: block;
text-decoration: none;
+ color: inherit;
}
.sf-toolbar-block span {
@@ -514,7 +515,7 @@ div.sf-toolbar .sf-toolbar-block a:hover {
@media (min-width: 1024px) {
.sf-toolbar-block .sf-toolbar-info-piece-additional,
.sf-toolbar-block .sf-toolbar-info-piece-additional-detail {
- display: inline-block;
+ display: inline;
}
.sf-toolbar-block .sf-toolbar-info-piece-additional:empty,
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 de6a01c90ec0e..d6c033d57245f 100644
--- a/src/Symfony/Component/BrowserKit/Client.php
+++ b/src/Symfony/Component/BrowserKit/Client.php
@@ -548,7 +548,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);
}
@@ -562,7 +562,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/Adapter/AbstractAdapter.php b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
index d93ae711bdac2..6897fbceee530 100644
--- a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
@@ -94,7 +94,7 @@ function ($deferred, $namespace, &$expiredIds) use ($getId) {
}
/**
- * Returns an ApcuAdapter if supported, a PhpFilesAdapter otherwise.
+ * Returns the best possible adapter that your runtime supports.
*
* Using ApcuAdapter makes system caches compatible with read-only filesystems.
*
@@ -108,16 +108,12 @@ function ($deferred, $namespace, &$expiredIds) use ($getId) {
*/
public static function createSystemCache($namespace, $defaultLifetime, $version, $directory, LoggerInterface $logger = null)
{
- if (null === self::$apcuSupported) {
- self::$apcuSupported = ApcuAdapter::isSupported();
+ $opcache = new PhpFilesAdapter($namespace, $defaultLifetime, $directory, true);
+ if (null !== $logger) {
+ $opcache->setLogger($logger);
}
- if (!self::$apcuSupported) {
- $opcache = new PhpFilesAdapter($namespace, $defaultLifetime, $directory, true);
- if (null !== $logger) {
- $opcache->setLogger($logger);
- }
-
+ if (!self::$apcuSupported = self::$apcuSupported ?? ApcuAdapter::isSupported()) {
return $opcache;
}
@@ -128,7 +124,7 @@ public static function createSystemCache($namespace, $defaultLifetime, $version,
$apcu->setLogger($logger);
}
- return $apcu;
+ return new ChainAdapter([$apcu, $opcache]);
}
public static function createConnection($dsn, array $options = [])
diff --git a/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php b/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php
index 8a7d147808bfe..0eceb6e572b39 100644
--- a/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php
+++ b/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php
@@ -22,7 +22,7 @@ protected function setUp()
{
parent::setUp();
- 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 1a13cbaae826e..3c8824869bd08 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 b83d69593715a..9c52323943b58 100644
--- a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php
+++ b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php
@@ -166,7 +166,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 0276c4fb6de65..8da2e257a88fe 100644
--- a/src/Symfony/Component/Cache/Traits/RedisTrait.php
+++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php
@@ -284,7 +284,7 @@ protected function doFetch(array $ids)
$result = [];
- if ($this->redis instanceof \Predis\Client) {
+ if ($this->redis instanceof \Predis\Client && $this->redis->getConnection() instanceof ClusterInterface) {
$values = $this->pipeline(function () use ($ids) {
foreach ($ids as $id) {
yield 'get' => [$id];
diff --git a/src/Symfony/Component/Config/Definition/ArrayNode.php b/src/Symfony/Component/Config/Definition/ArrayNode.php
index dd908c999e384..43697ef27943c 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());
@@ -383,7 +383,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 43c2b8b73188f..dca5687a2045e 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 4a3446f52ffe8..dd6ccd94a3585 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 f96d690589042..17df91f994483 100644
--- a/src/Symfony/Component/Console/Application.php
+++ b/src/Symfony/Component/Console/Application.php
@@ -199,6 +199,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 b68d9fe389a17..12a6cf22453e4 100644
--- a/src/Symfony/Component/Console/Helper/ProgressBar.php
+++ b/src/Symfony/Component/Console/Helper/ProgressBar.php
@@ -381,20 +381,17 @@ private function overwrite(string $message): void
$lines = floor(Helper::strlen($message) / $this->terminal->getWidth()) + $this->formatLineCount + 1;
$this->output->clear($lines);
} else {
- // 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 575c53458d72a..cf6447a4bcb50 100644
--- a/src/Symfony/Component/Console/Helper/QuestionHelper.php
+++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php
@@ -126,7 +126,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);
}
@@ -213,8 +213,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
@@ -267,6 +269,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;
@@ -339,7 +345,7 @@ private function getHiddenResponse(OutputInterface $output, $inputStream): strin
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 85ff6f91d8736..c56c20c684e80 100644
--- a/src/Symfony/Component/Console/Input/ArgvInput.php
+++ b/src/Symfony/Component/Console/Input/ArgvInput.php
@@ -257,8 +257,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 c7e5c987e5752..2189c46282264 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/CommandTester.php b/src/Symfony/Component/Console/Tester/CommandTester.php
index da51559faddca..57efc9a6754b2 100644
--- a/src/Symfony/Component/Console/Tester/CommandTester.php
+++ b/src/Symfony/Component/Console/Tester/CommandTester.php
@@ -60,9 +60,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/Tester/TesterTrait.php b/src/Symfony/Component/Console/Tester/TesterTrait.php
index e5df56d8357c7..7b5e128c49b63 100644
--- a/src/Symfony/Component/Console/Tester/TesterTrait.php
+++ b/src/Symfony/Component/Console/Tester/TesterTrait.php
@@ -126,7 +126,7 @@ public function setInputs(array $inputs)
*/
private function initOutput(array $options)
{
- $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/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php
index 2aaf9d7dae442..e9b1be32a647b 100644
--- a/src/Symfony/Component/Console/Tests/ApplicationTest.php
+++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php
@@ -968,6 +968,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 de3ec4559be4d..e3edbed02973f 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()) {
@@ -549,7 +586,7 @@ public function testChoiceOutputFormattingQuestionForUtf8Keys()
/**
* @expectedException \Symfony\Component\Console\Exception\RuntimeException
- * @expectedExceptionMessage Aborted
+ * @expectedExceptionMessage Aborted.
*/
public function testAskThrowsExceptionOnMissingInput()
{
@@ -559,7 +596,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 6d9b7417d679c..706629673c8da 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 35f1f55bef841..2816c4fba77b2 100644
--- a/src/Symfony/Component/Debug/DebugClassLoader.php
+++ b/src/Symfony/Component/Debug/DebugClassLoader.php
@@ -427,6 +427,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 b47e40211643d..78ac0240ab9ab 100644
--- a/src/Symfony/Component/Debug/ErrorHandler.php
+++ b/src/Symfony/Component/Debug/ErrorHandler.php
@@ -220,7 +220,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]) {
@@ -490,6 +490,11 @@ public function handleError($type, $message, $file, $line)
if ($this->isRecursive) {
$log = 0;
} 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;
@@ -498,7 +503,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 fc68b86460445..da02cf855f300 100644
--- a/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php
+++ b/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php
@@ -44,7 +44,7 @@ public function testIdempotence()
$functions = spl_autoload_functions();
foreach ($functions as $function) {
- if (is_array($function) && $function[0] instanceof DebugClassLoader) {
+ if (\is_array($function) && $function[0] instanceof DebugClassLoader) {
$reflClass = new \ReflectionClass($function[0]);
$reflProp = $reflClass->getProperty('classLoader');
$reflProp->setAccessible(true);
@@ -205,7 +205,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);
}
@@ -311,6 +311,11 @@ class_exists('Test\\'.__NAMESPACE__.'\\UseTraitWithInternalMethod', true);
$this->assertSame([], $deprecations);
}
+
+ public function testEvaluatedCode()
+ {
+ $this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\DefinitionInEvaluatedCode', true));
+ }
}
class ClassLoader
@@ -326,7 +331,7 @@ public function getClassMap()
public function findFile($class)
{
- $fixtureDir = __DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR;
+ $fixtureDir = __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'.\DIRECTORY_SEPARATOR;
if (__NAMESPACE__.'\TestingUnsilencing' === $class) {
eval('-- parse error --');
@@ -335,7 +340,7 @@ public function findFile($class)
} elseif (__NAMESPACE__.'\TestingCaseMismatch' === $class) {
eval('namespace '.__NAMESPACE__.'; class TestingCaseMisMatch {}');
} elseif (__NAMESPACE__.'\Fixtures\Psr4CaseMismatch' === $class) {
- return $fixtureDir.'psr4'.DIRECTORY_SEPARATOR.'Psr4CaseMismatch.php';
+ return $fixtureDir.'psr4'.\DIRECTORY_SEPARATOR.'Psr4CaseMismatch.php';
} elseif (__NAMESPACE__.'\Fixtures\NotPSR0' === $class) {
return $fixtureDir.'reallyNotPsr0.php';
} elseif (__NAMESPACE__.'\Fixtures\NotPSR0bis' === $class) {
diff --git a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php
index f3205840550c5..cf23365d4f0df 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;
/**
@@ -508,26 +508,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 {
@@ -536,13 +550,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 8929f26fe8118..f6995cc355031 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php
@@ -189,7 +189,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 8c34ce418269d..aeb96418112c9 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 cf7d96fceb3b9..2238779a25e2c 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php
@@ -119,19 +119,19 @@ 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;
}
$typeHint = ProxyHelper::getTypeHint($reflectionMethod, $parameter);
- if (array_key_exists($k = ltrim($typeHint, '\\').' $'.$parameter->name, $bindings)) {
+ if (\array_key_exists($k = ltrim($typeHint, '\\').' $'.$parameter->name, $bindings)) {
$arguments[$key] = $this->getBindingValue($bindings[$k]);
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 70f2556df5e1c..771c8f7b381f4 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php
@@ -77,7 +77,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 938c0a8e3c817..f1bafe9812d66 100644
--- a/src/Symfony/Component/DependencyInjection/Container.php
+++ b/src/Symfony/Component/DependencyInjection/Container.php
@@ -385,7 +385,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 b096237c3a6b7..a7a862cbbfab7 100644
--- a/src/Symfony/Component/DependencyInjection/Definition.php
+++ b/src/Symfony/Component/DependencyInjection/Definition.php
@@ -269,7 +269,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));
}
@@ -314,7 +314,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 b1a9016b0f67b..08c3e306fb12d 100644
--- a/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php
+++ b/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php
@@ -171,7 +171,7 @@ private function findNodes(): array
}
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/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
index 96b0812479467..f58e8b56d9fc2 100644
--- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
+++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
@@ -687,8 +687,8 @@ protected function {$methodName}($lazyInitialization)
$code .= $this->addServiceInclude($id, $definition);
if ($this->getProxyDumper()->isProxyCandidate($definition)) {
- $factoryCode = $asFile ? "\$this->load('%s.php', false)" : '$this->%s(false)';
- $code .= $this->getProxyDumper()->getProxyFactoryCode($definition, $id, sprintf($factoryCode, $methodName));
+ $factoryCode = $asFile ? ($definition->isShared() ? "\$this->load('%s.php', false)" : "\$this->factories['%2$s'](false)") : '$this->%s(false)';
+ $code .= $this->getProxyDumper()->getProxyFactoryCode($definition, $id, sprintf($factoryCode, $methodName, $this->export($id)));
}
if ($definition->isDeprecated()) {
@@ -862,7 +862,9 @@ private function generateServiceFiles(array $services)
}
$code[1] = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $code[1])));
$factory = sprintf('$this->factories%s[\'%s\']', $definition->isPublic() ? '' : "['service_container']", $id);
- $code[1] = sprintf("%s = function () {\n%s};\n\nreturn %1\$s();\n", $factory, $code[1]);
+ $lazyloadInitialization = $definition->isLazy() ? '$lazyLoad = true' : '';
+
+ $code[1] = sprintf("%s = function (%s) {\n%s};\n\nreturn %1\$s();\n", $factory, $lazyloadInitialization, $code[1]);
$code = $code[0].$code[1];
}
diff --git a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php
index 527060005d69d..03e822154d589 100644
--- a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php
+++ b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php
@@ -65,7 +65,7 @@ public function getEnv($prefix, $name, \Closure $getEnv)
if (!\is_array($array)) {
throw new RuntimeException(sprintf('Resolved value of "%s" did not result in an array value.', $next));
}
- if (!array_key_exists($key, $array)) {
+ if (!\array_key_exists($key, $array)) {
throw new RuntimeException(sprintf('Key "%s" not found in "%s" (resolved from "%s")', $key, json_encode($array), $next));
}
diff --git a/src/Symfony/Component/DependencyInjection/Extension/Extension.php b/src/Symfony/Component/DependencyInjection/Extension/Extension.php
index 25d07ff83b3af..1285334f58a77 100644
--- a/src/Symfony/Component/DependencyInjection/Extension/Extension.php
+++ b/src/Symfony/Component/DependencyInjection/Extension/Extension.php
@@ -127,7 +127,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 4d6a9f05a9395..2e36704080e6d 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 54c6fee5e2931..f9e0e472f2182 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
@@ -349,7 +349,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 3377b9aea47d7..9ad295fe1b48a 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
@@ -203,7 +203,7 @@ private function parseDefinitions(array $content, string $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']);
@@ -235,7 +235,7 @@ private function parseDefinitions(array $content, string $file)
*/
private function parseDefaults(array &$content, string $file): array
{
- if (!array_key_exists('_defaults', $content['services'])) {
+ if (!\array_key_exists('_defaults', $content['services'])) {
return [];
}
$defaults = $content['services']['_defaults'];
@@ -342,7 +342,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']);
@@ -426,7 +426,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']);
}
@@ -541,11 +541,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 84170577fc8d7..988dbf88cca64 100644
--- a/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php
+++ b/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php
@@ -68,7 +68,7 @@ public function get($name)
{
$name = (string) $name;
- if (!array_key_exists($name, $this->parameters)) {
+ if (!\array_key_exists($name, $this->parameters)) {
if (!$name) {
throw new ParameterNotFoundException($name);
}
@@ -119,7 +119,7 @@ public function set($name, $value)
*/
public function has($name)
{
- return array_key_exists((string) $name, $this->parameters);
+ return \array_key_exists((string) $name, $this->parameters);
}
/**
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
index b4acc949c2619..9a77e51548a5e 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
@@ -230,6 +230,24 @@ public function testDumpAsFiles()
$this->assertStringMatchesFormatFile(self::$fixturesPath.'/php/services9_as_files.txt', $dump);
}
+ public function testNonSharedLazyDumpAsFiles()
+ {
+ $container = include self::$fixturesPath.'/containers/container_non_shared_lazy.php';
+ $container->register('non_shared_foo', \Bar\FooLazyClass::class)
+ ->setFile(realpath(self::$fixturesPath.'/includes/foo_lazy.php'))
+ ->setShared(false)
+ ->setPublic(true)
+ ->setLazy(true);
+ $container->compile();
+ $dumper = new PhpDumper($container);
+ $dump = print_r($dumper->dump(['as_files' => true, 'file' => __DIR__]), true);
+
+ if ('\\' === \DIRECTORY_SEPARATOR) {
+ $dump = str_replace('\\\\Fixtures\\\\includes\\\\foo_lazy.php', '/Fixtures/includes/foo_lazy.php', $dump);
+ }
+ $this->assertStringMatchesFormatFile(self::$fixturesPath.'/php/services_non_shared_lazy_as_files.txt', $dump);
+ }
+
public function testServicesWithAnonymousFactories()
{
$container = include self::$fixturesPath.'/containers/container19.php';
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_non_shared_lazy.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_non_shared_lazy.php
new file mode 100644
index 0000000000000..58ff0bd5dcd57
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_non_shared_lazy.php
@@ -0,0 +1,5 @@
+ true,
+ 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true,
+];
+
+ [Container%s/getNonSharedFooService.php] => targetDirs[0].'/Fixtures/includes/foo_lazy.php');
+
+$this->factories['non_shared_foo'] = function ($lazyLoad = true) {
+ return new \Bar\FooLazyClass();
+};
+
+return $this->factories['non_shared_foo']();
+
+ [Container%s/ProjectServiceContainer.php] => targetDirs[0] = \dirname($containerDir);
+ for ($i = 1; $i <= 5; ++$i) {
+ $this->targetDirs[$i] = $dir = \dirname($dir);
+ }
+ $this->buildParameters = $buildParameters;
+ $this->containerDir = $containerDir;
+ $this->services = $this->privates = [];
+ $this->fileMap = [
+ 'non_shared_foo' => 'getNonSharedFooService.php',
+ ];
+
+ $this->aliases = [];
+ }
+
+ public function compile()
+ {
+ throw new LogicException('You cannot compile a dumped container that was already compiled.');
+ }
+
+ public function isCompiled()
+ {
+ return true;
+ }
+
+ public function getRemovedIds()
+ {
+ return require $this->containerDir.\DIRECTORY_SEPARATOR.'removed-ids.php';
+ }
+
+ protected function load($file, $lazyLoad = true)
+ {
+ return require $this->containerDir.\DIRECTORY_SEPARATOR.$file;
+ }
+}
+
+ [ProjectServiceContainer.php] => '%s',
+ 'container.build_id' => '%s',
+ 'container.build_time' => %d,
+], __DIR__.\DIRECTORY_SEPARATOR.'Container%s');
+
+)
\ No newline at end of file
diff --git a/src/Symfony/Component/DomCrawler/Crawler.php b/src/Symfony/Component/DomCrawler/Crawler.php
index d8a287a56e07f..5dbffe42c609a 100644
--- a/src/Symfony/Component/DomCrawler/Crawler.php
+++ b/src/Symfony/Component/DomCrawler/Crawler.php
@@ -971,7 +971,7 @@ private function relativize(string $xpath): string
$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 5c9249de28c53..24e2bb4324988 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');
- ($this->listener)($event, $eventName, $this->dispatcher ?: $dispatcher);
+ ($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 4c87b11a5b4cd..082084b4d93d1 100644
--- a/src/Symfony/Component/Filesystem/Filesystem.php
+++ b/src/Symfony/Component/Filesystem/Filesystem.php
@@ -138,8 +138,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
*/
@@ -194,7 +194,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)
{
@@ -215,7 +215,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)
{
@@ -242,7 +242,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)
{
@@ -524,14 +524,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 ab8f92b366299..83163f51d52eb 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__);
*
@@ -674,12 +674,15 @@ public function count()
private function searchInDirectory(string $dir): \Iterator
{
+ $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;
@@ -712,8 +715,8 @@ private function searchInDirectory(string $dir): \Iterator
$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);
@@ -746,8 +749,8 @@ private function searchInDirectory(string $dir): \Iterator
$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 || $this->reverseSorting) {
diff --git a/src/Symfony/Component/Finder/Tests/FinderTest.php b/src/Symfony/Component/Finder/Tests/FinderTest.php
index 1b50663e9f13c..9217cb7190b53 100644
--- a/src/Symfony/Component/Finder/Tests/FinderTest.php
+++ b/src/Symfony/Component/Finder/Tests/FinderTest.php
@@ -421,6 +421,59 @@ public function testIgnoreVCS()
]), $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',
+ 'qux',
+ 'qux/baz_100_1.py',
+ 'qux/baz_1_2.py',
+ 'qux_0_1.php',
+ 'qux_1000_1.php',
+ 'qux_1002_0.php',
+ 'qux_10_2.php',
+ 'qux_12_0.php',
+ 'qux_2_0.php',
+ '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',
+ 'qux',
+ 'qux/baz_100_1.py',
+ 'qux/baz_1_2.py',
+ 'qux_0_1.php',
+ 'qux_1000_1.php',
+ 'qux_1002_0.php',
+ 'qux_10_2.php',
+ 'qux_12_0.php',
+ 'qux_2_0.php',
+ 'test.php',
+ 'test.py',
+ 'toto',
+ 'toto/.git',
+ '.bar',
+ '.foo',
+ '.foo/.bar',
+ '.foo/bar',
+ 'foo bar',
+ ]), $finder->getIterator());
+ }
+
public function testIgnoreDotFiles()
{
$finder = $this->buildFinder();
@@ -496,6 +549,53 @@ public function testIgnoreDotFiles()
]), $finder->in(self::$tmpDir)->getIterator());
}
+ public function testIgnoreDotFilesCanBeDisabledAfterFirstIteration()
+ {
+ $finder = $this->buildFinder();
+ $finder->in(self::$tmpDir);
+
+ $this->assertIterator($this->toAbsolute([
+ 'foo',
+ 'foo/bar.tmp',
+ 'qux',
+ 'qux/baz_100_1.py',
+ 'qux/baz_1_2.py',
+ 'qux_0_1.php',
+ 'qux_1000_1.php',
+ 'qux_1002_0.php',
+ 'qux_10_2.php',
+ 'qux_12_0.php',
+ 'qux_2_0.php',
+ 'test.php',
+ 'test.py',
+ 'toto',
+ 'foo bar',
+ ]), $finder->getIterator());
+
+ $finder->ignoreDotFiles(false);
+ $this->assertIterator($this->toAbsolute([
+ 'foo',
+ 'foo/bar.tmp',
+ 'qux',
+ 'qux/baz_100_1.py',
+ 'qux/baz_1_2.py',
+ 'qux_0_1.php',
+ 'qux_1000_1.php',
+ 'qux_1002_0.php',
+ 'qux_10_2.php',
+ 'qux_12_0.php',
+ 'qux_2_0.php',
+ '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 5a67b7d23f872..27d8e70f2bc65 100644
--- a/src/Symfony/Component/Form/ButtonBuilder.php
+++ b/src/Symfony/Component/Form/ButtonBuilder.php
@@ -658,7 +658,7 @@ public function getAttributes()
*/
public function hasAttribute($name)
{
- return array_key_exists($name, $this->attributes);
+ return \array_key_exists($name, $this->attributes);
}
/**
@@ -671,7 +671,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;
}
/**
@@ -765,7 +765,7 @@ public function getOptions()
*/
public function hasOption($name)
{
- return array_key_exists($name, $this->options);
+ return \array_key_exists($name, $this->options);
}
/**
@@ -778,7 +778,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 b24f32a8cdbe4..a3150d275f558 100644
--- a/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php
+++ b/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php
@@ -78,7 +78,7 @@ public function __construct(iterable $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 30cc97551c2e0..b1a671d0c65eb 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 31d9aa485e676..18a288b444dd6 100644
--- a/src/Symfony/Component/Form/Console/Descriptor/JsonDescriptor.php
+++ b/src/Symfony/Component/Form/Console/Descriptor/JsonDescriptor.php
@@ -79,7 +79,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 4ea8ab6d834e5..48e0112e95d85 100644
--- a/src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php
+++ b/src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php
@@ -114,7 +114,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 e4f077d277a8a..392aa49e51a19 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.
@@ -42,8 +44,22 @@ public function __construct($grouping = false, $roundingMode = self::ROUND_DOWN)
*/
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 787805e2abcf2..ca341ac7120a0 100644
--- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php
+++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php
@@ -58,7 +58,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 f6f98e6de39ee..b019fff4a8e72 100644
--- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php
+++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php
@@ -181,9 +181,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 +226,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 31757e2887184..11ff3fa89b481 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 af52002ebd183..540cdb6f79db3 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
@@ -26,6 +28,16 @@ public function buildForm(FormBuilderInterface $builder, array $options)
$builder->addViewTransformer(new IntegerToLocalizedStringTransformer($options['grouping'], $options['rounding_mode']));
}
+ /**
+ * {@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 fe25f1f497753..7c5181e17d9b8 100644
--- a/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php
+++ b/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php
@@ -66,8 +66,10 @@ public function preSubmit(FormEvent $event)
if ($form->isRoot() && $form->getConfig()->getOption('compound') && !$postRequestSizeExceeded) {
$data = $event->getData();
- $csrfToken = new CsrfToken($this->tokenId, $data[$this->fieldName] ?? null);
- if (!isset($data[$this->fieldName]) || !$this->tokenManager->isTokenValid($csrfToken)) {
+ $csrfValue = \is_string($data[$this->fieldName] ?? null) ? $data[$this->fieldName] : null;
+ $csrfToken = new CsrfToken($this->tokenId, $csrfValue);
+
+ if (null === $csrfValue || !$this->tokenManager->isTokenValid($csrfToken)) {
$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 28cf9510d8be3..96baf9b58d7f5 100644
--- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php
+++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php
@@ -229,9 +229,6 @@ public function getData()
return $this->data;
}
- /**
- * @internal
- */
public function serialize()
{
foreach ($this->data['forms_by_hash'] as &$form) {
diff --git a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
index 6e6ef0c876781..db96bc731b9ba 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 eda99c0a62162..d322c1d8d99e5 100644
--- a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php
+++ b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php
@@ -50,7 +50,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 fb0ba683e82e7..17ecfd6f47741 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 344751b15976d..1a581d9b8e67e 100644
--- a/src/Symfony/Component/Form/FormConfigBuilder.php
+++ b/src/Symfony/Component/Form/FormConfigBuilder.php
@@ -418,7 +418,7 @@ public function getAttributes()
*/
public function hasAttribute($name)
{
- return array_key_exists($name, $this->attributes);
+ return \array_key_exists($name, $this->attributes);
}
/**
@@ -426,7 +426,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;
}
/**
@@ -513,7 +513,7 @@ public function getOptions()
*/
public function hasOption($name)
{
- return array_key_exists($name, $this->options);
+ return \array_key_exists($name, $this->options);
}
/**
@@ -521,7 +521,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 4b3866fe1cbe7..91e6faa9cc358 100644
--- a/src/Symfony/Component/Form/FormRenderer.php
+++ b/src/Symfony/Component/Form/FormRenderer.php
@@ -165,7 +165,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 fe0b3b7ab75a3..cfccf180b9abc 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 ab03646bbb332..4440776a8f3b8 100644
--- a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php
+++ b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php
@@ -19,6 +19,8 @@
abstract class AbstractLayoutTest extends FormIntegrationTestCase
{
+ use VersionAwareTest;
+
protected $csrfTokenManager;
protected $testableFeatures = [];
@@ -1796,6 +1798,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 1b202b5df1263..5d0518689a0f8 100644
--- a/src/Symfony/Component/Form/Tests/DependencyInjection/FormPassTest.php
+++ b/src/Symfony/Component/Form/Tests/DependencyInjection/FormPassTest.php
@@ -20,8 +20,9 @@
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\AbstractTypeExtension;
+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
@@ -71,8 +72,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);
@@ -379,19 +384,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 e96162d642ecf..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,74 +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();
- }
-
- /**
- * @return \PHPUnit_Framework_MockObject_MockObject
- */
- private function getForm(FormConfigInterface $config, bool $synchronized = true, bool $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());
}
@@ -99,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]);
@@ -123,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());
@@ -135,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]);
@@ -154,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);
}
/**
@@ -361,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()
@@ -389,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 8d54b064f82d8..dc723dc486869 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php
@@ -107,9 +107,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()
@@ -128,10 +126,10 @@ public function testReverseTransformWithGrouping()
$transformer = new IntegerToLocalizedStringTransformer(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'));
}
/**
@@ -147,10 +145,10 @@ public function testReverseTransformWithGroupingUsingLegacyConstructorSignature(
$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()
@@ -246,6 +244,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 14340989cecb4..6dde61e870414 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, 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 0498c764d102e..62a1ec286d793 100644
--- a/src/Symfony/Component/Form/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Tests\Extension\DependencyInjection;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension;
use Symfony\Component\Form\FormTypeGuesserChain;
@@ -21,9 +22,6 @@ class DependencyInjectionExtensionTest extends TestCase
{
public function testGetTypeExtensions()
{
- $container = $this->createContainerMock();
- $container->expects($this->never())->method('get');
-
$typeExtension1 = new TestTypeExtension();
$typeExtension2 = new TestTypeExtension();
$typeExtension3 = new OtherTypeExtension();
@@ -34,7 +32,7 @@ public function testGetTypeExtensions()
'other' => new \ArrayIterator([$typeExtension3, $typeExtension4]),
];
- $extension = new DependencyInjectionExtension($container, $extensions, []);
+ $extension = new DependencyInjectionExtension(new ContainerBuilder(), $extensions, []);
$this->assertTrue($extension->hasTypeExtensions('test'));
$this->assertTrue($extension->hasTypeExtensions('other'));
@@ -48,40 +46,28 @@ public function testGetTypeExtensions()
*/
public function testThrowExceptionForInvalidExtendedType()
{
- $container = $this->getMockBuilder('Psr\Container\ContainerInterface')->getMock();
- $container->expects($this->never())->method('get');
-
$extensions = [
'unmatched' => new \ArrayIterator([new TestTypeExtension()]),
];
- $extension = new DependencyInjectionExtension($container, $extensions, []);
+ $extension = new DependencyInjectionExtension(new ContainerBuilder(), $extensions, []);
$extension->getTypeExtensions('unmatched');
}
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());
}
-
- private function createContainerMock()
- {
- return $this->getMockBuilder('Psr\Container\ContainerInterface')
- ->setMethods(['get', 'has'])
- ->getMock();
- }
}
class TestTypeExtension extends AbstractTypeExtension
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 7910dd0b559e9..9a8b5c259ebdd 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();
}
private function getBuilder(string $name = 'name', string $dataClass = null, array $options = []): FormBuilder
@@ -682,18 +671,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 7d1733e20db76..34ff556d15300 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/UploadValidatorExtensionTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/UploadValidatorExtensionTest.php
@@ -21,18 +21,11 @@ class UploadValidatorExtensionTest extends TypeTestCase
{
public function testPostMaxSizeTranslation()
{
- $translator = $this->getMockBuilder(TranslatorInterface::class)->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'];
};
@@ -44,3 +37,25 @@ public function testPostMaxSizeTranslation()
$this->assertEquals('translated max {{ max }}!', $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 b37a72e47ebd7..fa9d17313cbe7 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 22e5fbb2d07c4..f685856584598 100644
--- a/src/Symfony/Component/HttpFoundation/RedirectResponse.php
+++ b/src/Symfony/Component/HttpFoundation/RedirectResponse.php
@@ -42,7 +42,7 @@ public function __construct(?string $url, int $status = 302, array $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/ResponseHeaderBag.php b/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php
index 9dd8908b2772d..9e07221933096 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 7b53e7fc766b9..ee33698cf0842 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 7890214e82533..162c4b5295849 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 782665caffbc6..6502f3d50155f 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 9674e3514b537..c6b7ce354cec4 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/DataCollector/DataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php
index 14ea230f57bdb..efe1705179b6d 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php
@@ -34,9 +34,6 @@ abstract class DataCollector implements DataCollectorInterface, \Serializable
*/
private $cloner;
- /**
- * @internal
- */
public function serialize()
{
$trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
@@ -45,9 +42,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 04ea44a073ec4..323ebf20547e7 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php
@@ -133,9 +133,6 @@ public function reset()
$this->clonesIndex = 0;
}
- /**
- * @internal
- */
public function serialize()
{
if ($this->clonesCount !== $this->clonesIndex) {
@@ -152,9 +149,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 dd00797f5f088..811f99bd9de8f 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php
@@ -79,13 +79,12 @@ public function onKernelException(GetResponseForExceptionEvent $event)
$this->logException($e, sprintf('Exception thrown when handling an exception (%s: %s at %s line %s)', $f->getClass(), $f->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 5ee09fb1a7f9c..c01b09559afd2 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 c5e351ca3a209..6737caee9e1a0 100644
--- a/src/Symfony/Component/HttpKernel/Kernel.php
+++ b/src/Symfony/Component/HttpKernel/Kernel.php
@@ -73,11 +73,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
private $requestStackSize = 0;
private $resetServices = false;
- const VERSION = '4.2.3';
- const VERSION_ID = 40203;
+ const VERSION = '4.2.4';
+ const VERSION_ID = 40204;
const MAJOR_VERSION = 4;
const MINOR_VERSION = 2;
- const RELEASE_VERSION = 3;
+ const RELEASE_VERSION = 4;
const EXTRA_VERSION = '';
const END_OF_MAINTENANCE = '07/2019';
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 d1fc3c01e0559..84940e63ae92e 100644
--- a/src/Symfony/Component/Intl/Data/Generator/LocaleDataGenerator.php
+++ b/src/Symfony/Component/Intl/Data/Generator/LocaleDataGenerator.php
@@ -164,7 +164,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 d3979fabff4a1..c668d9a3c13ca 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;
}
/**
@@ -484,7 +484,7 @@ public function getTextAttribute($attr)
* @param string $currency Parameter to receive the currency name (reference)
* @param int $position Offset to begin the parsing on return this value will hold the offset at which the parsing ended
*
- * @return bool|string The parsed numeric value of false on error
+ * @return bool|string The parsed numeric value or false on error
*
* @see http://www.php.net/manual/en/numberformatter.parsecurrency.php
*
@@ -502,7 +502,7 @@ public function parseCurrency($value, &$currency, &$position = null)
* @param int $type Type of the formatting, one of the format type constants. NumberFormatter::TYPE_DOUBLE by default.
* @param int $position Offset to begin the parsing on return this value will hold the offset at which the parsing ended
*
- * @return int|float|false The parsed value of false on error
+ * @return int|float|false The parsed value or false on error
*
* @see http://www.php.net/manual/en/numberformatter.parse.php
*/
diff --git a/src/Symfony/Component/Messenger/Transport/AmqpExt/Connection.php b/src/Symfony/Component/Messenger/Transport/AmqpExt/Connection.php
index cffc49df29c78..19415c7aede7c 100644
--- a/src/Symfony/Component/Messenger/Transport/AmqpExt/Connection.php
+++ b/src/Symfony/Component/Messenger/Transport/AmqpExt/Connection.php
@@ -110,7 +110,7 @@ public static function fromDsn(string $dsn, array $options = [], bool $debug = f
private static function normalizeQueueArguments(array $arguments): array
{
foreach (self::ARGUMENTS_AS_INTEGER as $key) {
- if (!array_key_exists($key, $arguments)) {
+ if (!\array_key_exists($key, $arguments)) {
continue;
}
@@ -253,6 +253,6 @@ private function clear(): void
private function shouldSetup(): bool
{
- return !array_key_exists('auto-setup', $this->connectionCredentials) || !\in_array($this->connectionCredentials['auto-setup'], [false, 'false'], true);
+ return !\array_key_exists('auto-setup', $this->connectionCredentials) || !\in_array($this->connectionCredentials['auto-setup'], [false, 'false'], true);
}
}
diff --git a/src/Symfony/Component/OptionsResolver/Debug/OptionsResolverIntrospector.php b/src/Symfony/Component/OptionsResolver/Debug/OptionsResolverIntrospector.php
index 3867447a30ecb..ccce082fed29d 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 6532fe0a0ab8d..eb0a6c2480601 100644
--- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php
+++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php
@@ -219,7 +219,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;
}
@@ -259,7 +259,7 @@ public function setDefaults(array $defaults)
*/
public function hasDefault($option)
{
- return array_key_exists($option, $this->defaults);
+ return \array_key_exists($option, $this->defaults);
}
/**
@@ -324,7 +324,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);
}
/**
@@ -800,7 +800,7 @@ public function offsetGet($option/*, bool $triggerDeprecation = true*/)
$triggerDeprecation = 1 === \func_num_args() || \func_get_arg(1);
// Shortcut for resolved options
- if (isset($this->resolved[$option]) || array_key_exists($option, $this->resolved)) {
+ if (isset($this->resolved[$option]) || \array_key_exists($option, $this->resolved)) {
if ($triggerDeprecation && isset($this->deprecated[$option]) && (isset($this->given[$option]) || $this->calling) && \is_string($this->deprecated[$option])) {
@trigger_error(strtr($this->deprecated[$option], ['%name%' => $option]), E_USER_DEPRECATED);
}
@@ -809,7 +809,7 @@ public function offsetGet($option/*, bool $triggerDeprecation = true*/)
}
// Check whether the option is set at all
- if (!isset($this->defaults[$option]) && !array_key_exists($option, $this->defaults)) {
+ if (!isset($this->defaults[$option]) && !\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))));
}
@@ -1033,7 +1033,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 f063e8f007438..6bf6e210968e6 100644
--- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php
+++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php
@@ -164,7 +164,7 @@ private static function throwInvalidArgumentException($message, $trace, $i, $pro
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];
@@ -266,7 +266,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 ac883e7c042a0..0eb9f63a54856 100644
--- a/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php
+++ b/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php
@@ -105,7 +105,7 @@ private function extract(string $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 fab14ca366373..1bc720bbbaadb 100644
--- a/src/Symfony/Component/Routing/Generator/UrlGenerator.php
+++ b/src/Symfony/Component/Routing/Generator/UrlGenerator.php
@@ -156,9 +156,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]]]));
}
@@ -212,7 +212,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/PhpMatcherDumper.php b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php
index 61b2ed320a223..754e903d89ed0 100644
--- a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php
+++ b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php
@@ -159,6 +159,7 @@ private function groupStaticRoutes(RouteCollection $collection): array
foreach ($collection->all() as $name => $route) {
$compiledRoute = $route->compile();
+ $staticPrefix = rtrim($compiledRoute->getStaticPrefix(), '/');
$hostRegex = $compiledRoute->getHostRegex();
$regex = $compiledRoute->getRegex();
if ($hasTrailingSlash = '/' !== $route->getPath()) {
@@ -173,9 +174,9 @@ private function groupStaticRoutes(RouteCollection $collection): array
if ($hasTrailingSlash) {
$url = substr($url, 0, -1);
}
- foreach ($dynamicRegex as list($hostRx, $rx)) {
- if (preg_match($rx, $url) && (!$host || !$hostRx || preg_match($hostRx, $host))) {
- $dynamicRegex[] = [$hostRegex, $regex];
+ foreach ($dynamicRegex as list($hostRx, $rx, $prefix)) {
+ if (('' === $prefix || 0 === strpos($url, $prefix)) && preg_match($rx, $url) && (!$host || !$hostRx || preg_match($hostRx, $host))) {
+ $dynamicRegex[] = [$hostRegex, $regex, $staticPrefix];
$dynamicRoutes->add($name, $route);
continue 2;
}
@@ -183,7 +184,7 @@ private function groupStaticRoutes(RouteCollection $collection): array
$staticRoutes[$url][$name] = [$route, $hasTrailingSlash];
} else {
- $dynamicRegex[] = [$hostRegex, $regex];
+ $dynamicRegex[] = [$hostRegex, $regex, $staticPrefix];
$dynamicRoutes->add($name, $route);
}
}
diff --git a/src/Symfony/Component/Routing/RequestContext.php b/src/Symfony/Component/Routing/RequestContext.php
index dc3e4be1e7bc4..591dd15938695 100644
--- a/src/Symfony/Component/Routing/RequestContext.php
+++ b/src/Symfony/Component/Routing/RequestContext.php
@@ -306,7 +306,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 58675902a1c58..4e0463b57578c 100644
--- a/src/Symfony/Component/Routing/Route.php
+++ b/src/Symfony/Component/Routing/Route.php
@@ -327,7 +327,7 @@ public function getOption($name)
*/
public function hasOption($name)
{
- return array_key_exists($name, $this->options);
+ return \array_key_exists($name, $this->options);
}
/**
@@ -396,7 +396,7 @@ public function getDefault($name)
*/
public function hasDefault($name)
{
- return array_key_exists($name, $this->defaults);
+ return \array_key_exists($name, $this->defaults);
}
/**
@@ -481,7 +481,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);
}
/**
@@ -192,7 +192,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 058085e349c95..8509844f9a17b 100644
--- a/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php
+++ b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php
@@ -42,7 +42,7 @@ class AccessDecisionManager implements AccessDecisionManagerInterface
public function __construct(iterable $voters = [], string $strategy = self::STRATEGY_AFFIRMATIVE, bool $allowIfAllAbstainDecisions = false, bool $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 a774d7e11c777..8695ba3401d72 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 982b16f012a32..0547bed04ad39 100644
--- a/src/Symfony/Component/Security/Guard/composer.json
+++ b/src/Symfony/Component/Security/Guard/composer.json
@@ -17,7 +17,7 @@
],
"require": {
"php": "^7.1.3",
- "symfony/security-core": "~3.4|~4.0",
+ "symfony/security-core": "~3.4.22|^4.2.3",
"symfony/security-http": "~3.4|~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 f6fd0c1ec405f..a8ccee0f7d557 100644
--- a/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php
+++ b/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php
@@ -97,7 +97,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 80082b0099c78..4aff3b45811fa 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 {
@@ -165,7 +169,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 f6ce869b299d1..2b8ab8d09f578 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 2be2cc220763e..f26b72fc148e1 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/Mapping/ClassDiscriminatorFromClassMetadata.php b/src/Symfony/Component/Serializer/Mapping/ClassDiscriminatorFromClassMetadata.php
index 1178f03cfb9cc..23554ffda98ab 100644
--- a/src/Symfony/Component/Serializer/Mapping/ClassDiscriminatorFromClassMetadata.php
+++ b/src/Symfony/Component/Serializer/Mapping/ClassDiscriminatorFromClassMetadata.php
@@ -55,7 +55,7 @@ public function getMappingForMappedObject($object): ?ClassDiscriminatorMapping
}
$cacheKey = \is_object($object) ? \get_class($object) : $object;
- if (!array_key_exists($cacheKey, $this->mappingForMappedObjectCache)) {
+ if (!\array_key_exists($cacheKey, $this->mappingForMappedObjectCache)) {
$this->mappingForMappedObjectCache[$cacheKey] = $this->resolveMappingForMappedObject($object);
}
diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
index 300c7f1b3c681..e1f5d136dd17e 100644
--- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
+++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
@@ -398,14 +398,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 ($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;
@@ -417,9 +417,9 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref
// Don't run set for a parameter passed to the constructor
$params[] = $this->denormalizeParameter($reflectionClass, $constructorParameter, $paramName, $parameterData, $context, $format);
unset($data[$key]);
- } elseif (array_key_exists($key, $context[static::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class] ?? [])) {
+ } elseif (\array_key_exists($key, $context[static::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class] ?? [])) {
$params[] = $context[static::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class][$key];
- } elseif (array_key_exists($key, $this->defaultContext[self::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class] ?? [])) {
+ } elseif (\array_key_exists($key, $this->defaultContext[self::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class] ?? [])) {
$params[] = $this->defaultContext[self::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class][$key];
} elseif ($constructorParameter->isDefaultValueAvailable()) {
$params[] = $constructorParameter->getDefaultValue();
diff --git a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php
index d3de806e186aa..51ad59a7e782e 100644
--- a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php
+++ b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php
@@ -157,6 +157,7 @@ private function formatDateTimeErrors(array $errors)
private function getTimezone(array $context)
{
$dateTimeZone = $context[self::TIMEZONE_KEY] ?? $this->defaultContext[self::TIMEZONE_KEY];
+
if (null === $dateTimeZone) {
return null;
}
diff --git a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php
index 530f168c6a7b5..94ed6d8b6394d 100644
--- a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php
+++ b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php
@@ -113,7 +113,7 @@ protected function extractAttributes($object, $format = null, array $context = [
protected function getAttributeValue($object, $attribute, $format = null, array $context = [])
{
$cacheKey = \get_class($object);
- if (!array_key_exists($cacheKey, $this->discriminatorCache)) {
+ if (!\array_key_exists($cacheKey, $this->discriminatorCache)) {
$this->discriminatorCache[$cacheKey] = null;
if (null !== $this->classDiscriminatorResolver) {
$mapping = $this->classDiscriminatorResolver->getMappingForMappedObject($object);
diff --git a/src/Symfony/Component/Templating/TemplateReference.php b/src/Symfony/Component/Templating/TemplateReference.php
index 18c6bda2df2b3..ddff4db857654 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/DataCollector/TranslationDataCollector.php b/src/Symfony/Component/Translation/DataCollector/TranslationDataCollector.php
old mode 100755
new mode 100644
diff --git a/src/Symfony/Component/Translation/Dumper/FileDumper.php b/src/Symfony/Component/Translation/Dumper/FileDumper.php
index 57ce9d21414ef..8457ecc7ecd7a 100644
--- a/src/Symfony/Component/Translation/Dumper/FileDumper.php
+++ b/src/Symfony/Component/Translation/Dumper/FileDumper.php
@@ -63,7 +63,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 8ee8d5a009a1f..8d2694f24e2d2 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']);
}
@@ -141,7 +141,11 @@ private function dumpXliff2($defaultLocale, MessageCatalogue $messages, $domain,
$xliff->setAttribute('trgLang', str_replace('_', '-', $messages->getLocale()));
$xliffFile = $xliff->appendChild($dom->createElement('file'));
- $xliffFile->setAttribute('id', $domain.'.'.$messages->getLocale());
+ if (MessageCatalogue::INTL_DOMAIN_SUFFIX === substr($domain, -($suffixLength = \strlen(MessageCatalogue::INTL_DOMAIN_SUFFIX)))) {
+ $xliffFile->setAttribute('id', substr($domain, 0, -$suffixLength).'.'.$messages->getLocale());
+ } else {
+ $xliffFile->setAttribute('id', $domain.'.'.$messages->getLocale());
+ }
foreach ($messages->all($domain) as $source => $target) {
$translation = $dom->createElement('unit');
@@ -200,6 +204,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/Translation/Tests/Dumper/XliffFileDumperTest.php b/src/Symfony/Component/Translation/Tests/Dumper/XliffFileDumperTest.php
index c634a27936711..6377132f7009b 100644
--- a/src/Symfony/Component/Translation/Tests/Dumper/XliffFileDumperTest.php
+++ b/src/Symfony/Component/Translation/Tests/Dumper/XliffFileDumperTest.php
@@ -54,6 +54,21 @@ public function testFormatCatalogueXliff2()
);
}
+ public function testFormatIcuCatalogueXliff2()
+ {
+ $catalogue = new MessageCatalogue('en_US');
+ $catalogue->add([
+ 'foo' => 'bar',
+ ], 'messages'.MessageCatalogue::INTL_DOMAIN_SUFFIX);
+
+ $dumper = new XliffFileDumper();
+
+ $this->assertStringEqualsFile(
+ __DIR__.'/../fixtures/resources-2.0+intl-icu.xlf',
+ $dumper->formatCatalogue($catalogue, 'messages'.MessageCatalogue::INTL_DOMAIN_SUFFIX, ['default_locale' => 'fr_FR', 'xliff_version' => '2.0'])
+ );
+ }
+
public function testFormatCatalogueWithCustomToolInfo()
{
$options = [
diff --git a/src/Symfony/Component/Translation/Tests/fixtures/resources-2.0+intl-icu.xlf b/src/Symfony/Component/Translation/Tests/fixtures/resources-2.0+intl-icu.xlf
new file mode 100644
index 0000000000000..6294f162fd7b0
--- /dev/null
+++ b/src/Symfony/Component/Translation/Tests/fixtures/resources-2.0+intl-icu.xlf
@@ -0,0 +1,11 @@
+
+
+
+
+
+ foo
+ bar
+
+
+
+
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 731501e08db14..cbc3e198a35bf 100644
--- a/src/Symfony/Component/Validator/Constraints/CollectionValidator.php
+++ b/src/Symfony/Component/Validator/Constraints/CollectionValidator.php
@@ -51,7 +51,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/CountValidator.php b/src/Symfony/Component/Validator/Constraints/CountValidator.php
index adedacdc6247f..301358bcf714c 100644
--- a/src/Symfony/Component/Validator/Constraints/CountValidator.php
+++ b/src/Symfony/Component/Validator/Constraints/CountValidator.php
@@ -13,6 +13,7 @@
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
+use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
/**
diff --git a/src/Symfony/Component/Validator/Constraints/Email.php b/src/Symfony/Component/Validator/Constraints/Email.php
index 7d13eceaf7a4e..c6c760c2c0d74 100644
--- a/src/Symfony/Component/Validator/Constraints/Email.php
+++ b/src/Symfony/Component/Validator/Constraints/Email.php
@@ -76,19 +76,19 @@ class Email extends Constraint
public function __construct($options = null)
{
- if (\is_array($options) && array_key_exists('strict', $options)) {
+ if (\is_array($options) && \array_key_exists('strict', $options)) {
@trigger_error(sprintf('The "strict" property is deprecated since Symfony 4.1. Use "mode"=>"%s" instead.', self::VALIDATION_MODE_STRICT), E_USER_DEPRECATED);
}
- if (\is_array($options) && array_key_exists('checkMX', $options)) {
+ if (\is_array($options) && \array_key_exists('checkMX', $options)) {
@trigger_error('The "checkMX" option is deprecated since Symfony 4.2.', E_USER_DEPRECATED);
}
- if (\is_array($options) && array_key_exists('checkHost', $options)) {
+ if (\is_array($options) && \array_key_exists('checkHost', $options)) {
@trigger_error('The "checkHost" option is deprecated since Symfony 4.2.', E_USER_DEPRECATED);
}
- if (\is_array($options) && array_key_exists('mode', $options) && !\in_array($options['mode'], self::$validationModes, true)) {
+ if (\is_array($options) && \array_key_exists('mode', $options) && !\in_array($options['mode'], self::$validationModes, true)) {
throw new \InvalidArgumentException('The "mode" parameter value is not valid.');
}
diff --git a/src/Symfony/Component/Validator/Constraints/IbanValidator.php b/src/Symfony/Component/Validator/Constraints/IbanValidator.php
index d6530ab101702..e927f013aa052 100644
--- a/src/Symfony/Component/Validator/Constraints/IbanValidator.php
+++ b/src/Symfony/Component/Validator/Constraints/IbanValidator.php
@@ -182,7 +182,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/Constraints/Url.php b/src/Symfony/Component/Validator/Constraints/Url.php
index 2a237bffad3cc..815d73be7cd09 100644
--- a/src/Symfony/Component/Validator/Constraints/Url.php
+++ b/src/Symfony/Component/Validator/Constraints/Url.php
@@ -109,10 +109,10 @@ class Url extends Constraint
public function __construct($options = null)
{
if (\is_array($options)) {
- if (array_key_exists('checkDNS', $options)) {
+ if (\array_key_exists('checkDNS', $options)) {
@trigger_error(sprintf('The "checkDNS" option in "%s" is deprecated since Symfony 4.1. Its false-positive rate is too high to be relied upon.', self::class), E_USER_DEPRECATED);
}
- if (array_key_exists('dnsMessage', $options)) {
+ if (\array_key_exists('dnsMessage', $options)) {
@trigger_error(sprintf('The "dnsMessage" option in "%s" is deprecated since Symfony 4.1.', self::class), E_USER_DEPRECATED);
}
}
diff --git a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php
index afbe72b73508e..6e0256c29018e 100644
--- a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php
+++ b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php
@@ -366,7 +366,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.