diff --git a/CHANGELOG-3.4.md b/CHANGELOG-3.4.md
index 8e7a8406ff391..7fc92f86eb326 100644
--- a/CHANGELOG-3.4.md
+++ b/CHANGELOG-3.4.md
@@ -7,6 +7,35 @@ in 3.4 minor versions.
To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash
To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v3.4.0...v3.4.1
+* 3.4.40 (2020-04-28)
+
+ * bug #36566 [PhpUnitBridge] Use COMPOSER_BINARY env var if available (fancyweb)
+ * bug #36560 [YAML] escape DEL(\x7f) (sdkawata)
+ * bug #36539 [PhpUnitBridge] fix compatibility with phpunit 9 (garak)
+ * bug #36555 [Cache] skip APCu in chains when the backend is disabled (nicolas-grekas)
+ * bug #36523 [Form] apply automatically step=1 for datetime-local input (ottaviano)
+ * bug #36498 [Security/Core] fix escape for username in LdapBindAuthenticationProvider.php (stoccc)
+ * bug #36506 [FrameworkBundle] Fix session.attribute_bag service definition (fancyweb)
+ * bug #36490 [HttpFoundation] workaround PHP bug in the session module (nicolas-grekas)
+ * bug #36483 [SecurityBundle] fix accepting env vars in remember-me configurations (zek)
+ * bug #36343 [Form] Fixed handling groups sequence validation (HeahDude)
+ * bug #36460 [Cache] Avoid memory leak in TraceableAdapter::reset() (lyrixx)
+ * bug #36411 [Form] RepeatedType should always have inner types mapped (biozshock)
+ * bug #36441 [DI] fix loading defaults when using the PHP-DSL (nicolas-grekas)
+ * bug #36434 [HttpKernel] silence E_NOTICE triggered since PHP 7.4 (xabbuh)
+ * bug #36365 [Validator] Fixed default group for nested composite constraints (HeahDude)
+ * bug #35591 [Validator] do not merge constraints within interfaces (greedyivan)
+ * bug #36375 [Workflow] Use a strict comparison when retrieving raw marking in MarkingStore (lyrixx)
+ * bug #36305 [PropertyInfo][ReflectionExtractor] Check the array mutator prefixes last when the property is singular (fancyweb)
+ * bug #35656 [HttpFoundation] Fixed session migration with custom cookie lifetime (Guite)
+ * bug #36315 [WebProfilerBundle] Support for Content Security Policy style-src-elem and script-src-elem in WebProfiler (ampaze)
+ * bug #36286 [Validator] Allow URL-encoded special characters in basic auth part of URLs (cweiske)
+ * bug #36332 [Serializer] Fix unitialized properties (from PHP 7.4.2) when serializing context for the cache key (alanpoulain)
+ * bug #36239 [HttpKernel][LoggerDataCollector] Prevent keys collisions in the sanitized logs processing (fancyweb)
+ * bug #36245 [Validator] Fixed calling getters before resolving groups (HeahDude)
+ * bug #36252 [Security/Http] Allow setting cookie security settings for delete_cookies (wouterj)
+ * bug #36261 [FrameworkBundle] revert to legacy wiring of the session when circular refs are detected (nicolas-grekas)
+
* 3.4.39 (2020-03-30)
* bug #36216 [Validator] Assert Valid with many groups (phucwan91)
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 53a2faec2f45f..449973d7578eb 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -24,10 +24,10 @@ Symfony is the result of the work of many people who made the code better
- Kris Wallsmith (kriswallsmith)
- Yonel Ceruto (yonelceruto)
- Hugo Hamon (hhamon)
+ - Wouter de Jong (wouterj)
+ - Thomas Calvet (fancyweb)
- Abdellatif Ait boudad (aitboudad)
- Samuel ROZE (sroze)
- - Thomas Calvet (fancyweb)
- - Wouter de Jong (wouterj)
- Romain Neutron (romain)
- Pascal Borreli (pborreli)
- Joseph Bielawski (stloyd)
@@ -37,8 +37,8 @@ Symfony is the result of the work of many people who made the code better
- Jules Pietri (heah)
- Hamza Amrouche (simperfit)
- Martin Hasoň (hason)
- - Jeremy Mikola (jmikola)
- Jérémy DERUSSÉ (jderusse)
+ - Jeremy Mikola (jmikola)
- Jean-François Simon (jfsimon)
- Benjamin Eberlei (beberlei)
- Igor Wiedler (igorw)
@@ -50,9 +50,9 @@ Symfony is the result of the work of many people who made the code better
- Lynn van der Berg (kjarli)
- Diego Saint Esteben (dosten)
- Matthias Pigulla (mpdude)
+ - Pierre du Plessis (pierredup)
- Alexandre Salomé (alexandresalome)
- William Durand (couac)
- - Pierre du Plessis (pierredup)
- ornicar
- Dany Maillard (maidmaid)
- Francis Besset (francisbesset)
@@ -69,10 +69,10 @@ Symfony is the result of the work of many people who made the code better
- Gabriel Ostrolucký (gadelat)
- Miha Vrhovnik
- David Maicher (dmaicher)
- - Diego Saint Esteben (dii3g0)
- Gábor Egyed (1ed)
- - Titouan Galopin (tgalopin)
+ - Diego Saint Esteben (dii3g0)
- Jan Schädlich (jschaedl)
+ - Titouan Galopin (tgalopin)
- Konstantin Kudryashov (everzet)
- Bilal Amarni (bamarni)
- Mathieu Piot (mpiot)
@@ -110,6 +110,7 @@ Symfony is the result of the work of many people who made the code better
- Baptiste Clavié (talus)
- Michal Piotrowski (eventhorizon)
- Tim Nagel (merk)
+ - Sebastiaan Stok (sstok)
- Chris Wilkinson (thewilkybarkid)
- Brice BERNARD (brikou)
- marc.weistroff
@@ -119,7 +120,6 @@ Symfony is the result of the work of many people who made the code better
- lenar
- Alexander Schwenn (xelaris)
- Włodzimierz Gajda (gajdaw)
- - Sebastiaan Stok (sstok)
- Adrien Brault (adrienbrault)
- Jacob Dreesen (jdreesen)
- Florian Voutzinos (florianv)
@@ -139,18 +139,18 @@ Symfony is the result of the work of many people who made the code better
- Przemysław Bogusz (przemyslaw-bogusz)
- Eric GELOEN (gelo)
- Lars Strojny (lstrojny)
+ - Massimiliano Arione (garak)
- Jannik Zschiesche (apfelbox)
- Robert Schönthal (digitalkaoz)
- Gregor Harlan (gharlan)
- Florian Lonqueu-Brochard (florianlb)
+ - Alexander Schranz (alexander-schranz)
- Gabriel Caruso (carusogabriel)
- Stefano Sala (stefano.sala)
- Evgeniy (ewgraf)
- - Massimiliano Arione (garak)
- Julien Falque (julienfalque)
- Vincent AUBERT (vincent)
- Juti Noppornpitak (shiroyuki)
- - Alexander Schranz (alexander-schranz)
- Anthony MARTIN (xurudragon)
- Tigran Azatyan (tigranazatyan)
- Sebastian Hörl (blogsh)
@@ -176,6 +176,7 @@ Symfony is the result of the work of many people who made the code better
- Richard van Laak (rvanlaak)
- Richard Shank (iampersistent)
- Thomas Rabaix (rande)
+ - Ahmed TAILOULOUTE (ahmedtai)
- Vincent Touzet (vincenttouzet)
- jeremyFreeAgent (jeremyfreeagent)
- Rouven Weßling (realityking)
@@ -198,6 +199,7 @@ Symfony is the result of the work of many people who made the code better
- James Halsall (jaitsu)
- Matthieu Napoli (mnapoli)
- Florent Mata (fmata)
+ - Antoine Makdessi (amakdessi)
- Warnar Boekkooi (boekkooi)
- Dmitrii Chekaliuk (lazyhammer)
- Clément JOBEILI (dator)
@@ -213,15 +215,18 @@ Symfony is the result of the work of many people who made the code better
- Dennis Benkert (denderello)
- DQNEO
- Andre Rømcke (andrerom)
+ - Saif (╯°□°)╯ (azjezz)
- mcfedr (mcfedr)
- Gary PEGEOT (gary-p)
- Ruben Gonzalez (rubenrua)
- Benjamin Dulau (dbenjamin)
+ - Jan Rosier (rosier)
- Andreas Braun
- Mathieu Lemoine (lemoinem)
- Christian Schmidt
- Andreas Hucks (meandmymonkey)
- Tom Van Looy (tvlooy)
+ - Guillaume Pédelagrabe
- Noel Guilbert (noel)
- Anthony GRASSIOT (antograssiot)
- Stadly
@@ -233,7 +238,7 @@ Symfony is the result of the work of many people who made the code better
- Nikolay Labinskiy (e-moe)
- Martin Schuhfuß (usefulthink)
- apetitpa
- - Antoine Makdessi (amakdessi)
+ - Maxime Helias (maxhelias)
- Matthieu Bontemps (mbontemps)
- apetitpa
- Pierre Minnieur (pminnieur)
@@ -243,14 +248,13 @@ Symfony is the result of the work of many people who made the code better
- Laurent VOULLEMIER (lvo)
- Michael Lee (zerustech)
- Matthieu Auger (matthieuauger)
- - Ahmed TAILOULOUTE (ahmedtai)
+ - Mathias Arlaud (mtarld)
- Leszek Prabucki (l3l0)
- Fabien Bourigault (fbourigault)
- François Zaninotto (fzaninotto)
- Dustin Whittle (dustinwhittle)
- jeff
- John Kary (johnkary)
- - Jan Rosier (rosier)
- Justin Hileman (bobthecow)
- Blanchon Vincent (blanchonvincent)
- Michele Orselli (orso)
@@ -270,7 +274,6 @@ Symfony is the result of the work of many people who made the code better
- Marcel Beerta (mazen)
- Christopher Hertel (chertel)
- Ruud Kamphuis (ruudk)
- - Maxime Helias (maxhelias)
- Pavel Batanov (scaytrase)
- Mantis Development
- David Prévot
@@ -283,7 +286,6 @@ Symfony is the result of the work of many people who made the code better
- Lorenz Schori
- Sébastien Lavoie (lavoiesl)
- Dariusz
- - Saif (╯°□°)╯ (azjezz)
- Dmitrii Poddubnyi (karser)
- Michael Babker (mbabker)
- Francois Zaninotto
@@ -307,7 +309,6 @@ Symfony is the result of the work of many people who made the code better
- Arjen Brouwer (arjenjb)
- Katsuhiro OGAWA
- Patrick McDougle (patrick-mcdougle)
- - Guillaume Pédelagrabe
- Alif Rachmawadi
- Anton Chernikov (anton_ch1989)
- Kristen Gilden (kgilden)
@@ -336,9 +337,9 @@ Symfony is the result of the work of many people who made the code better
- Jeroen Spee (jeroens)
- Nikita Konstantinov
- Wodor Wodorski
+ - Olivier Dolbeau (odolbeau)
- Thomas Lallement (raziel057)
- Colin O'Dell (colinodell)
- - Mathias Arlaud (mtarld)
- Giorgio Premi
- renanbr
- Alex Rock (pierstoval)
@@ -364,6 +365,7 @@ Symfony is the result of the work of many people who made the code better
- Vilius Grigaliūnas
- David Badura (davidbadura)
- Chad Sikorra (chadsikorra)
+ - Alan Poulain (alanpoulain)
- Chris Smith (cs278)
- Thomas Bisignani (toma)
- Florian Klein (docteurklein)
@@ -413,7 +415,6 @@ Symfony is the result of the work of many people who made the code better
- Thomas Royer (cydonia7)
- Nicolas LEFEVRE (nicoweb)
- alquerci
- - Olivier Dolbeau (odolbeau)
- Oleg Andreyev
- Mateusz Sip (mateusz_sip)
- Francesco Levorato
@@ -426,6 +427,7 @@ Symfony is the result of the work of many people who made the code better
- Tomasz Kowalczyk (thunderer)
- Artur Eshenbrener
- Timo Bakx (timobakx)
+ - Antonio Pauletich (x-coder264)
- Thomas Perez (scullwm)
- Felix Labrecque
- Yaroslav Kiliba
@@ -443,7 +445,6 @@ Symfony is the result of the work of many people who made the code better
- Eduardo Gulias (egulias)
- giulio de donato (liuggio)
- ShinDarth
- - Alan Poulain
- Stéphane PY (steph_py)
- Philipp Kräutli (pkraeutli)
- Grzegorz (Greg) Zdanowski (kiler129)
@@ -540,10 +541,10 @@ Symfony is the result of the work of many people who made the code better
- Martijn Cuppens
- Vlad Gregurco (vgregurco)
- Boris Vujicic (boris.vujicic)
+ - Artem Lopata
- Chris Sedlmayr (catchamonkey)
- Kamil Kokot (pamil)
- Seb Koelen
- - Antonio Pauletich (x-coder264)
- Christoph Mewes (xrstf)
- Vitaliy Tverdokhlib (vitaliytv)
- Ariel Ferrandini (aferrandini)
@@ -553,14 +554,17 @@ Symfony is the result of the work of many people who made the code better
- Jonas Flodén (flojon)
- Tobias Weichart
- Gonzalo Vilaseca (gonzalovilaseca)
+ - Daniel STANCU
- Tarmo Leppänen (tarlepp)
- Marcin Sikoń (marphi)
- Dominik Zogg (dominik.zogg)
- Marek Pietrzak
- Luc Vieillescazes (iamluc)
- franek (franek)
+ - soyuka
- Raulnet
- Christian Wahler
+ - Giso Stallenberg (gisostallenberg)
- Gintautas Miselis
- Rob Bast
- Roberto Espinoza (respinoza)
@@ -599,6 +603,7 @@ Symfony is the result of the work of many people who made the code better
- Philipp Rieber (bicpi)
- Manuel de Ruiter (manuel)
- Nathanael Noblet (gnat)
+ - Dimitri Gritsajuk (ottaviano)
- nikos.sotiropoulos
- Eduardo Oliveira (entering)
- Ilya Antipenko (aivus)
@@ -744,6 +749,7 @@ Symfony is the result of the work of many people who made the code better
- Benjamin Cremer (bcremer)
- Javier López (loalf)
- Reinier Kip
+ - Jérôme Tamarelle (jtamarelle-prismamedia)
- Geoffrey Brier (geoffrey-brier)
- Alexandre Parent
- Vladimir Tsykun
@@ -778,7 +784,6 @@ Symfony is the result of the work of many people who made the code better
- Miquel Rodríguez Telep (mrtorrent)
- Sergey Kolodyazhnyy (skolodyazhnyy)
- umpirski
- - Artem Lopata
- M. Vondano
- Quentin de Longraye (quentinus95)
- Chris Heng (gigablah)
@@ -786,10 +791,10 @@ Symfony is the result of the work of many people who made the code better
- Richard Bradley
- Ulumuddin Yunus (joenoez)
- rtek
+ - Ivan Grigoriev
- Johann Saunier (prophet777)
- Sergey (upyx)
- Andreas Erhard
- - Giso Stallenberg (gisostallenberg)
- Michael Devery (mickadoo)
- Antoine Corcy
- Ahmed Ashraf (ahmedash95)
@@ -810,6 +815,7 @@ Symfony is the result of the work of many people who made the code better
- Cameron Porter
- Hossein Bukhamsin
- Oliver Hoff
+ - William Arslett
- Christian Sciberras (uuf6429)
- Disparity
- origaminal
@@ -856,7 +862,6 @@ Symfony is the result of the work of many people who made the code better
- Markus Fasselt (digilist)
- Julien DIDIER (juliendidier)
- Dominik Ritter (dritter)
- - Dimitri Gritsajuk (ottaviano)
- Sebastian Grodzicki (sgrodzicki)
- Mohamed Gamal
- Jeroen van den Enden (stoefke)
@@ -871,6 +876,7 @@ Symfony is the result of the work of many people who made the code better
- Yuen-Chi Lian
- Tarjei Huse (tarjei)
- Besnik Br
+ - Axel Guckelsberger (guite)
- Jose Gonzalez
- Jonathan (jls-esokia)
- Oleksii Zhurbytskyi
@@ -881,12 +887,14 @@ Symfony is the result of the work of many people who made the code better
- Jakub Kulhan (jakubkulhan)
- Shaharia Azam
- avorobiev
+ - stoccc
- Grégoire Penverne (gpenverne)
- Venu
- Lars Vierbergen
- Jonatan Männchen
- Dennis Hotson
- Andrew Tchircoff (andrewtch)
+ - Ahmed Raafat
- michaelwilliams
- Martin Kirilov
- 1emming
@@ -894,6 +902,7 @@ Symfony is the result of the work of many people who made the code better
- Tri Pham (phamuyentri)
- Jordan Deitch
- Casper Valdemar Poulsen
+ - Laurent Masforné (heisenberg)
- Josiah (josiah)
- Guillaume Verstraete (versgui)
- Greg ORIOL
@@ -961,10 +970,8 @@ Symfony is the result of the work of many people who made the code better
- Laurent Bassin (lbassin)
- andrey1s
- Abhoryo
- - Daniel STANCU
- Fabian Vogler (fabian)
- Korvin Szanto
- - soyuka
- Stéphan Kochen
- Arjan Keeman
- Alaattin Kahramanlar (alaattin)
@@ -1133,6 +1140,7 @@ Symfony is the result of the work of many people who made the code better
- Nicolas Le Goff (nlegoff)
- Ben Oman
- Chris de Kok
+ - Eduard Bulava (nonanerz)
- Lorenzo Millucci
- Andreas Kleemann
- Manuele Menozzi
@@ -1150,6 +1158,7 @@ Symfony is the result of the work of many people who made the code better
- hamza
- dantleech
- Bastien DURAND (deamon)
+ - Kajetan Kołtuniak (kajtii)
- Sander Goossens (sandergo90)
- Rudy Onfroy
- Tero Alén (tero)
@@ -1237,7 +1246,6 @@ Symfony is the result of the work of many people who made the code better
- Benjamin Paap (benjaminpaap)
- Claus Due (namelesscoder)
- Christian
- - William Arslett
- Denis Golubovskiy (bukashk0zzz)
- Sergii Smertin (nfx)
- Mikkel Paulson
@@ -1246,6 +1254,7 @@ Symfony is the result of the work of many people who made the code better
- Marc Duboc (icemad)
- Matthias Krauser (mkrauser)
- Martynas Narbutas
+ - Nilmar Sanchez Muguercia
- Toon Verwerft (veewee)
- Bailey Parker
- Eddie Jaoude
@@ -1289,6 +1298,7 @@ Symfony is the result of the work of many people who made the code better
- Stephen Clouse
- e-ivanov
- Michał (bambucha15)
+ - Benjamin Dos Santos
- Einenlum
- Jérémy Jarrié (gagnar)
- Jochen Bayer (jocl)
@@ -1344,6 +1354,7 @@ Symfony is the result of the work of many people who made the code better
- Klaus Purer
- arnaud (arnooo999)
- Gilles Doge (gido)
+ - Oscar Esteve (oesteve)
- abulford
- Philipp Kretzschmar
- antograssiot
@@ -1367,7 +1378,6 @@ Symfony is the result of the work of many people who made the code better
- Derek Lambert
- MightyBranch
- Kacper Gunia (cakper)
- - Jérôme Tamarelle (jtamarelle-prismamedia)
- Peter Thompson (petert82)
- error56
- Felicitus
@@ -1392,6 +1402,7 @@ Symfony is the result of the work of many people who made the code better
- Nyro (nyro)
- Marco
- Marc Torres
+ - Mark Spink
- Alberto Aldegheri
- Dmitri Petmanson
- heccjj
@@ -1412,7 +1423,6 @@ Symfony is the result of the work of many people who made the code better
- David Négrier (moufmouf)
- Quique Porta (quiqueporta)
- mohammadreza honarkhah
- - stoccc
- Andrea Quintino (dirk39)
- Tomasz Szymczyk (karion)
- Alex Vasilchenko
@@ -1468,7 +1478,6 @@ Symfony is the result of the work of many people who made the code better
- Walter Dal Mut (wdalmut)
- abluchet
- Ruud Arentsen
- - Ahmed Raafat
- Harald Tollefsen
- Matthieu
- Albin Kerouaton
@@ -1504,7 +1513,6 @@ Symfony is the result of the work of many people who made the code better
- Antal Áron (antalaron)
- Vašek Purchart (vasek-purchart)
- Janusz Jabłoński (yanoosh)
- - Ivan Grigoriev
- Fleuv
- Sandro Hopf
- Łukasz Makuch
@@ -1522,6 +1530,7 @@ Symfony is the result of the work of many people who made the code better
- Philip Frank
- David Brooks
- Lance McNearney
+ - Serhiy Lunak (slunak)
- Giorgio Premi
- Aurélien Fontaine
- ncou
@@ -1531,6 +1540,7 @@ Symfony is the result of the work of many people who made the code better
- Matt Daum (daum)
- Alberto Pirovano (geezmo)
- Pete Mitchell (peterjmit)
+ - phuc vo (phucwan)
- Tom Corrigan (tomcorrigan)
- Luis Galeas
- Bogdan Scordaliu
@@ -1543,7 +1553,6 @@ Symfony is the result of the work of many people who made the code better
- WedgeSama
- Felds Liscia
- Chihiro Adachi (chihiro-adachi)
- - Axel Guckelsberger (guite)
- Raphaëll Roussel
- Tadcka
- Beth Binkovitz
@@ -1651,6 +1660,7 @@ Symfony is the result of the work of many people who made the code better
- Flavian (2much)
- Gautier Deuette
- mike
+ - tadas
- Kirk Madera
- Keith Maika
- Mephistofeles
@@ -1834,6 +1844,7 @@ Symfony is the result of the work of many people who made the code better
- alefranz
- David Barratt
- Andrea Giannantonio
+ - Dries Vints
- Pavel.Batanov
- avi123
- Pavel Prischepa
@@ -1900,6 +1911,7 @@ Symfony is the result of the work of many people who made the code better
- Yannick Warnier (ywarnier)
- Kevin Decherf
- Jason Woods
+ - Christian Weiske
- Maria Grazia Patteri
- klemens
- dened
@@ -2149,6 +2161,7 @@ Symfony is the result of the work of many people who made the code better
- Evgeniy Tetenchuk
- Sjoerd Adema
- Shrey Puranik
+ - Evgeniy Koval
- Lars Moelleken
- dasmfm
- Mathias Geat
@@ -2208,6 +2221,7 @@ Symfony is the result of the work of many people who made the code better
- Niklas Keller
- Andras Debreczeni
- Vladimir Sazhin
+ - Michel Bardelmeijer
- Tomas Kmieliauskas
- Billie Thompson
- lol768
@@ -2241,6 +2255,7 @@ Symfony is the result of the work of many people who made the code better
- Michael Schneider
- Cédric Bertolini
- n-aleha
+ - Talha Zekeriya Durmuş
- Anatol Belski
- Anderson Müller
- Şəhriyar İmanov
@@ -2268,6 +2283,7 @@ Symfony is the result of the work of many people who made the code better
- Neophy7e
- bokonet
- Arrilot
+ - ampaze
- Markus Staab
- Pierre-Louis LAUNAY
- djama
@@ -2417,6 +2433,7 @@ Symfony is the result of the work of many people who made the code better
- Michal Čihař (mcihar)
- Matt Drollette (mdrollette)
- Adam Monsen (meonkeys)
+ - Mike Milano (mmilano)
- diego aguiar (mollokhan)
- Hugo Monteiro (monteiro)
- Ala Eddine Khefifi (nayzo)
diff --git a/phpunit b/phpunit
index c89d2e400b602..fbce26d8edcca 100755
--- a/phpunit
+++ b/phpunit
@@ -1,7 +1,7 @@
#!/usr/bin/env php
bindParam(':id', $sessionId, \PDO::PARAM_STR);
$stmt->execute();
} catch (\Exception $e) {
- throw new \RuntimeException(sprintf('Exception was thrown when trying to delete a session: %s.', $e->getMessage()), 0, $e);
+ throw new \RuntimeException('Exception was thrown when trying to delete a session: '.$e->getMessage(), 0, $e);
}
return true;
@@ -115,7 +115,7 @@ public function gc($maxlifetime)
$stmt->bindValue(':time', time() - $maxlifetime, \PDO::PARAM_INT);
$stmt->execute();
} catch (\Exception $e) {
- throw new \RuntimeException(sprintf('Exception was thrown when trying to delete expired sessions: %s.', $e->getMessage()), 0, $e);
+ throw new \RuntimeException('Exception was thrown when trying to delete expired sessions: '.$e->getMessage(), 0, $e);
}
return true;
@@ -142,7 +142,7 @@ public function read($sessionId)
return '';
} catch (\Exception $e) {
- throw new \RuntimeException(sprintf('Exception was thrown when trying to read the session data: %s.', $e->getMessage()), 0, $e);
+ throw new \RuntimeException('Exception was thrown when trying to read the session data: '.$e->getMessage(), 0, $e);
}
}
@@ -212,7 +212,7 @@ public function write($sessionId, $data)
}
}
} catch (\Exception $e) {
- throw new \RuntimeException(sprintf('Exception was thrown when trying to write the session data: %s.', $e->getMessage()), 0, $e);
+ throw new \RuntimeException('Exception was thrown when trying to write the session data: '.$e->getMessage(), 0, $e);
}
return true;
diff --git a/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php b/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php
index 1fe8a5c53a5ab..180341012fd1c 100644
--- a/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php
+++ b/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php
@@ -92,7 +92,7 @@ public function refreshUser(UserInterface $user)
$refreshedUser = $repository->find($id);
if (null === $refreshedUser) {
- throw new UsernameNotFoundException(sprintf('User with id %s not found.', json_encode($id)));
+ throw new UsernameNotFoundException('User with id '.json_encode($id).' not found.');
}
}
diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php
index 69bbcfc09eab0..e260fb8dd6854 100644
--- a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php
+++ b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php
@@ -46,9 +46,12 @@ class SymfonyTestsListenerTrait
public function __construct(array $mockedNamespaces = array())
{
if (class_exists('PHPUnit_Util_Blacklist')) {
- \PHPUnit_Util_Blacklist::$blacklistedClassNames['\Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait'] = 2;
+ \PHPUnit_Util_Blacklist::$blacklistedClassNames[__CLASS__] = 2;
+ } elseif (method_exists(Blacklist::class, 'addDirectory')) {
+ (new BlackList())->getBlacklistedDirectories();
+ Blacklist::addDirectory(\dirname((new \ReflectionClass(__CLASS__))->getFileName(), 2));
} else {
- Blacklist::$blacklistedClassNames['\Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait'] = 2;
+ Blacklist::$blacklistedClassNames[__CLASS__] = 2;
}
$warn = false;
diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit
index d2c23f2025b74..92d4d6994af9c 100755
--- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit
+++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit
@@ -65,11 +65,13 @@ foreach ($defaultEnvs as $envName => $envValue) {
}
}
-$COMPOSER = file_exists($COMPOSER = $oldPwd.'/composer.phar')
- || ($COMPOSER = rtrim('\\' === DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer.phar`) : `which composer.phar 2> /dev/null`))
- || ($COMPOSER = rtrim('\\' === DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer`) : `which composer 2> /dev/null`))
- ? (file_get_contents($COMPOSER, false, null, 0, 18) === '#!/usr/bin/env php' ? $PHP : '').' '.escapeshellarg($COMPOSER) // detect shell wrappers by looking at the shebang
- : 'composer';
+if (false === $COMPOSER = \getenv('COMPOSER_BINARY')) {
+ $COMPOSER = file_exists($COMPOSER = $oldPwd.'/composer.phar')
+ || ($COMPOSER = rtrim('\\' === DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer.phar`) : `which composer.phar 2> /dev/null`))
+ || ($COMPOSER = rtrim('\\' === DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer`) : `which composer 2> /dev/null`))
+ ? (file_get_contents($COMPOSER, false, null, 0, 18) === '#!/usr/bin/env php' ? $PHP : '').' '.escapeshellarg($COMPOSER) // detect shell wrappers by looking at the shebang
+ : 'composer';
+}
if (false === $SYMFONY_PHPUNIT_REMOVE = getenv('SYMFONY_PHPUNIT_REMOVE')) {
$SYMFONY_PHPUNIT_REMOVE = 'phpspec/prophecy symfony/yaml';
@@ -126,6 +128,10 @@ if (!class_exists('SymfonyBlacklistPhpunit', false)) {
if (class_exists('PHPUnit_Util_Blacklist')) {
PHPUnit_Util_Blacklist::$blacklistedClassNames['SymfonyBlacklistPhpunit'] = 1;
PHPUnit_Util_Blacklist::$blacklistedClassNames['SymfonyBlacklistSimplePhpunit'] = 1;
+} elseif (method_exists('PHPUnit\Util\Blacklist', 'addDirectory')) {
+ (new PHPUnit\Util\BlackList())->getBlacklistedDirectories();
+ PHPUnit\Util\Blacklist::addDirectory(\dirname((new \ReflectionClass('SymfonyBlacklistPhpunit'))->getFileName()));
+ PHPUnit\Util\Blacklist::addDirectory(\dirname((new \ReflectionClass('SymfonyBlacklistSimplePhpunit'))->getFileName()));
} else {
PHPUnit\Util\Blacklist::$blacklistedClassNames['SymfonyBlacklistPhpunit'] = 1;
PHPUnit\Util\Blacklist::$blacklistedClassNames['SymfonyBlacklistSimplePhpunit'] = 1;
diff --git a/src/Symfony/Bridge/PhpUnit/composer.json b/src/Symfony/Bridge/PhpUnit/composer.json
index f7d9492613ab6..30b3cc23f63d8 100644
--- a/src/Symfony/Bridge/PhpUnit/composer.json
+++ b/src/Symfony/Bridge/PhpUnit/composer.json
@@ -24,7 +24,7 @@
"symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader"
},
"conflict": {
- "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0|<6.4,>=6.0"
+ "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0|<6.4,>=6.0|9.1.2"
},
"autoload": {
"files": [ "bootstrap.php" ],
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CacheCollectorPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CacheCollectorPass.php
index 7f8494d98e0ad..6bb614489eeb8 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CacheCollectorPass.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CacheCollectorPass.php
@@ -44,7 +44,9 @@ public function process(ContainerBuilder $container)
$recorder = new Definition(is_subclass_of($definition->getClass(), TagAwareAdapterInterface::class) ? TraceableTagAwareAdapter::class : TraceableAdapter::class);
$recorder->setTags($definition->getTags());
- $recorder->setPublic($definition->isPublic());
+ if (!$definition->isPublic() || !$definition->isPrivate()) {
+ $recorder->setPublic($definition->isPublic());
+ }
$recorder->setArguments([new Reference($innerId = $id.'.recorder_inner')]);
$definition->setTags([]);
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/SessionPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/SessionPass.php
new file mode 100644
index 0000000000000..0f4950615fbce
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/SessionPass.php
@@ -0,0 +1,50 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * @internal to be removed in 6.0
+ */
+class SessionPass implements CompilerPassInterface
+{
+ public function process(ContainerBuilder $container)
+ {
+ if (!$container->hasDefinition('session')) {
+ return;
+ }
+
+ $bags = [
+ 'session.flash_bag' => $container->hasDefinition('session.flash_bag') ? $container->getDefinition('session.flash_bag') : null,
+ 'session.attribute_bag' => $container->hasDefinition('session.attribute_bag') ? $container->getDefinition('session.attribute_bag') : null,
+ ];
+
+ foreach ($container->getDefinition('session')->getArguments() as $v) {
+ if (!$v instanceof Reference || !isset($bags[$bag = (string) $v]) || !\is_array($factory = $bags[$bag]->getFactory())) {
+ continue;
+ }
+
+ if ([0, 1] !== array_keys($factory) || !$factory[0] instanceof Reference || 'session' !== (string) $factory[0]) {
+ continue;
+ }
+
+ if ('get'.ucfirst(substr($bag, 8, -4)).'Bag' !== $factory[1]) {
+ continue;
+ }
+
+ $bags[$bag]->setFactory(null);
+ }
+ }
+}
diff --git a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
index b472275ff530a..f8bf78d859054 100644
--- a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
+++ b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
@@ -22,6 +22,7 @@
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\DataCollectorTranslatorPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\LoggingTranslatorPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ProfilerPass;
+use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\SessionPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TemplatingPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\UnusedTagsPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\WorkflowGuardListenerPass;
@@ -125,6 +126,7 @@ public function build(ContainerBuilder $container)
$this->addCompilerPassIfExists($container, FormPass::class);
$container->addCompilerPass(new WorkflowGuardListenerPass());
$container->addCompilerPass(new ResettableServicePass());
+ $container->addCompilerPass(new SessionPass());
if ($container->getParameter('kernel.debug')) {
$container->addCompilerPass(new AddDebugLogProcessorPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -32);
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml
index 9f3a5b78c1e2e..9080af9379899 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml
@@ -41,7 +41,8 @@
-
+
+ attributes
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/SessionPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/SessionPassTest.php
new file mode 100644
index 0000000000000..afc6f9b4b2577
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/SessionPassTest.php
@@ -0,0 +1,44 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\SessionPass;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+class SessionPassTest extends TestCase
+{
+ public function testProcess()
+ {
+ $arguments = [
+ new Reference('session.flash_bag'),
+ new Reference('session.attribute_bag'),
+ ];
+ $container = new ContainerBuilder();
+ $container
+ ->register('session')
+ ->setArguments($arguments);
+ $container
+ ->register('session.flash_bag')
+ ->setFactory([new Reference('session'), 'getFlashBag']);
+ $container
+ ->register('session.attribute_bag')
+ ->setFactory([new Reference('session'), 'getAttributeBag']);
+
+ (new SessionPass())->process($container);
+
+ $this->assertSame($arguments, $container->getDefinition('session')->getArguments());
+ $this->assertNull($container->getDefinition('session.flash_bag')->getFactory());
+ $this->assertNull($container->getDefinition('session.attribute_bag')->getFactory());
+ }
+}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/src/CustomPathBundle.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/src/CustomPathBundle.php
index 166b606a459e2..a4049b8690522 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/src/CustomPathBundle.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/src/CustomPathBundle.php
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
-namespace Symfony\Bundle\FrameworkBundle\Tests;
+namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\CustomPathBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/TestBundle.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/TestBundle.php
index 2f090b2de8d53..c58b25066bf4a 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/TestBundle.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/TestBundle.php
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
-namespace Symfony\Bundle\FrameworkBundle\Tests;
+namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\TestBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php
index df96cf2db78f5..1020c86e8e583 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php
@@ -273,6 +273,8 @@ private function addFirewallsSection(ArrayNodeDefinition $rootNode, array $facto
->children()
->scalarNode('path')->defaultNull()->end()
->scalarNode('domain')->defaultNull()->end()
+ ->scalarNode('secure')->defaultFalse()->end()
+ ->scalarNode('samesite')->defaultNull()->end()
->end()
->end()
->end()
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php
index 624deb0283fd7..cd7202019c2eb 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php
@@ -146,8 +146,6 @@ public function addConfiguration(NodeDefinition $node)
foreach ($this->options as $name => $value) {
if (\is_bool($value)) {
$builder->booleanNode($name)->defaultValue($value);
- } elseif (\is_int($value)) {
- $builder->integerNode($name)->defaultValue($value);
} else {
$builder->scalarNode($name)->defaultValue($value);
}
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Csp/ContentSecurityPolicyHandler.php b/src/Symfony/Bundle/WebProfilerBundle/Csp/ContentSecurityPolicyHandler.php
index a38e7c686fd0a..e62895fe6d2b2 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Csp/ContentSecurityPolicyHandler.php
+++ b/src/Symfony/Bundle/WebProfilerBundle/Csp/ContentSecurityPolicyHandler.php
@@ -128,7 +128,7 @@ private function updateCspHeaders(Response $response, array $nonces = [])
$headers = $this->getCspHeaders($response);
foreach ($headers as $header => $directives) {
- foreach (['script-src' => 'csp_script_nonce', 'style-src' => 'csp_style_nonce'] as $type => $tokenName) {
+ foreach (['script-src' => 'csp_script_nonce', 'script-src-elem' => 'csp_script_nonce', 'style-src' => 'csp_style_nonce', 'style-src-elem' => 'csp_style_nonce'] as $type => $tokenName) {
if ($this->authorizesInline($directives, $type)) {
continue;
}
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/memory.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/memory.html.twig
index 268f8fdc7e3f6..49878a72d5d65 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/memory.html.twig
+++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/memory.html.twig
@@ -5,18 +5,18 @@
{% set status_color = (collector.memory / 1024 / 1024) > 50 ? 'yellow' : '' %}
{{ include('@WebProfiler/Icon/memory.svg') }}
{{ '%.1f'|format(collector.memory / 1024 / 1024) }}
- MB
+ MiB
{% endset %}
{% set text %}
Peak memory usage
- {{ '%.1f'|format(collector.memory / 1024 / 1024) }} MB
+ {{ '%.1f'|format(collector.memory / 1024 / 1024) }} MiB
PHP memory limit
- {{ collector.memoryLimit == -1 ? 'Unlimited' : '%.0f MB'|format(collector.memoryLimit / 1024 / 1024) }}
+ {{ collector.memoryLimit == -1 ? 'Unlimited' : '%.0f MiB'|format(collector.memoryLimit / 1024 / 1024) }}
{% endset %}
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig
index dd95b511b40ea..a9fd19225d2d6 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig
+++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig
@@ -64,7 +64,7 @@
{% if profile.collectors.memory %}
- {{ '%.2f'|format(profile.collectors.memory.memory / 1024 / 1024) }} MB
+ {{ '%.2f'|format(profile.collectors.memory.memory / 1024 / 1024) }} MiB
Peak memory usage
{% endif %}
@@ -386,7 +386,7 @@
ctx.fillStyle = "#444";
ctx.font = "12px sans-serif";
text = event.name;
- ms = " " + (event.duration < 1 ? event.duration : parseInt(event.duration, 10)) + " ms / " + event.memory + " MB";
+ ms = " " + (event.duration < 1 ? event.duration : parseInt(event.duration, 10)) + " ms / " + event.memory + " MiB";
if (x + event.starttime * ratio + ctx.measureText(text + ms).width > width) {
ctx.textAlign = "end";
ctx.font = "10px sans-serif";
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/Csp/ContentSecurityPolicyHandlerTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/Csp/ContentSecurityPolicyHandlerTest.php
index acccc7cbfb6d2..349db2aaf75b4 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Tests/Csp/ContentSecurityPolicyHandlerTest.php
+++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/Csp/ContentSecurityPolicyHandlerTest.php
@@ -41,7 +41,7 @@ public function testOnKernelResponse($nonce, $expectedNonce, Request $request, R
$this->assertFalse($response->headers->has('X-SymfonyProfiler-Style-Nonce'));
foreach ($expectedCsp as $header => $value) {
- $this->assertSame($value, $response->headers->get($header));
+ $this->assertSame($value, $response->headers->get($header), $header);
}
}
@@ -131,7 +131,7 @@ public function provideRequestAndResponsesForOnKernelResponse()
['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce],
$this->createRequest(),
$this->createResponse(['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'']),
- ['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'; style-src \'self\' domain.com \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'; style-src \'self\' domain-report-only.com \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'X-Content-Security-Policy' => null],
+ ['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\' domain.com \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src \'self\' domain.com \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src-elem \'self\' domain.com \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\' domain-report-only.com \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src \'self\' domain-report-only.com \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src-elem \'self\' domain-report-only.com \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'X-Content-Security-Policy' => null],
],
[
$nonce,
diff --git a/src/Symfony/Bundle/WebServerBundle/Command/ServerLogCommand.php b/src/Symfony/Bundle/WebServerBundle/Command/ServerLogCommand.php
index 5c9ce910ddf54..75d94321f4f0b 100644
--- a/src/Symfony/Bundle/WebServerBundle/Command/ServerLogCommand.php
+++ b/src/Symfony/Bundle/WebServerBundle/Command/ServerLogCommand.php
@@ -99,7 +99,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
}
if (!$socket = stream_socket_server($host, $errno, $errstr)) {
- throw new RuntimeException(sprintf('Server start failed on "%s": %s %s.', $host, $errstr, $errno));
+ throw new RuntimeException(sprintf('Server start failed on "%s": '.$errstr.' '.$errno, $host));
}
foreach ($this->getLogs($socket) as $clientId => $message) {
diff --git a/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php b/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php
index 2ab686a561956..d052317678c80 100644
--- a/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php
+++ b/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php
@@ -59,7 +59,7 @@ private function getManifestPath($path)
$this->manifestData = json_decode(file_get_contents($this->manifestPath), true);
if (0 < json_last_error()) {
- throw new \RuntimeException(sprintf('Error parsing JSON from asset manifest file "%s" - %s.', $this->manifestPath, json_last_error_msg()));
+ throw new \RuntimeException(sprintf('Error parsing JSON from asset manifest file "%s": '.json_last_error_msg(), $this->manifestPath));
}
}
diff --git a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
index dad0dc8f66f55..ce1262ae17557 100644
--- a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
@@ -14,7 +14,6 @@
use Psr\Cache\CacheItemInterface;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerInterface;
-use Psr\Log\NullLogger;
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\ResettableInterface;
@@ -116,14 +115,12 @@ public static function createSystemCache($namespace, $defaultLifetime, $version,
if (null !== $logger) {
$fs->setLogger($logger);
}
- if (!self::$apcuSupported) {
+ if (!self::$apcuSupported || (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && !filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN))) {
return $fs;
}
$apcu = new ApcuAdapter($namespace, (int) $defaultLifetime / 5, $version);
- if ('cli' === \PHP_SAPI && !filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN)) {
- $apcu->setLogger(new NullLogger());
- } elseif (null !== $logger) {
+ if (null !== $logger) {
$apcu->setLogger($logger);
}
diff --git a/src/Symfony/Component/Cache/Adapter/ChainAdapter.php b/src/Symfony/Component/Cache/Adapter/ChainAdapter.php
index 0080db711bce7..c2c9f4a8008ac 100644
--- a/src/Symfony/Component/Cache/Adapter/ChainAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/ChainAdapter.php
@@ -46,6 +46,9 @@ public function __construct(array $adapters, $defaultLifetime = 0)
if (!$adapter instanceof CacheItemPoolInterface) {
throw new InvalidArgumentException(sprintf('The class "%s" does not implement the "%s" interface.', \get_class($adapter), CacheItemPoolInterface::class));
}
+ if (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && $adapter instanceof ApcuAdapter && !filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN)) {
+ continue; // skip putting APCu in the chain when the backend is disabled
+ }
if ($adapter instanceof AdapterInterface) {
$this->adapters[] = $adapter;
diff --git a/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php b/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php
index 4aed2d7181f83..cc855c132a83c 100644
--- a/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php
@@ -191,15 +191,11 @@ public function prune()
*/
public function reset()
{
- if (!$this->pool instanceof ResettableInterface) {
- return;
- }
- $event = $this->start(__FUNCTION__);
- try {
+ if ($this->pool instanceof ResettableInterface) {
$this->pool->reset();
- } finally {
- $event->end = microtime(true);
}
+
+ $this->clearCalls();
}
public function getCalls()
diff --git a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php
index 3f4fb5243d0e2..3ff28d25b3576 100644
--- a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php
+++ b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php
@@ -272,7 +272,7 @@ private function checkResultCode($result)
return $result;
}
- throw new CacheException(sprintf('MemcachedAdapter client error: %s.', strtolower($this->client->getResultMessage())));
+ throw new CacheException('MemcachedAdapter client error: '.strtolower($this->client->getResultMessage()));
}
/**
diff --git a/src/Symfony/Component/Config/Definition/BaseNode.php b/src/Symfony/Component/Config/Definition/BaseNode.php
index bf57a9fa17bc5..1f6ef7f834b8c 100644
--- a/src/Symfony/Component/Config/Definition/BaseNode.php
+++ b/src/Symfony/Component/Config/Definition/BaseNode.php
@@ -335,7 +335,7 @@ final public function finalize($value)
} catch (Exception $e) {
throw $e;
} catch (\Exception $e) {
- throw new InvalidConfigurationException(sprintf('Invalid configuration for path "%s": %s.', $this->getPath(), $e->getMessage()), $e->getCode(), $e);
+ throw new InvalidConfigurationException(sprintf('Invalid configuration for path "%s": '.$e->getMessage(), $this->getPath()), $e->getCode(), $e);
}
}
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
index 5ca2b2246b76b..27969e7067254 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
@@ -126,7 +126,7 @@ protected function getConstructor(Definition $definition, $required)
throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.', $this->currentId, $class));
}
} catch (\ReflectionException $e) {
- throw new RuntimeException(sprintf('Invalid service "%s": %s.', $this->currentId, lcfirst(rtrim($e->getMessage(), '.'))));
+ throw new RuntimeException(sprintf('Invalid service "%s": '.lcfirst($e->getMessage()), $this->currentId));
}
if (!$r = $r->getConstructor()) {
if ($required) {
diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/PrototypeConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/PrototypeConfigurator.php
index d80c8b13389f2..3d844798d431a 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/PrototypeConfigurator.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/PrototypeConfigurator.php
@@ -45,10 +45,13 @@ class PrototypeConfigurator extends AbstractServiceConfigurator
public function __construct(ServicesConfigurator $parent, PhpFileLoader $loader, Definition $defaults, $namespace, $resource, $allowParent)
{
$definition = new Definition();
- $definition->setPublic($defaults->isPublic());
+ if (!$defaults->isPublic() || !$defaults->isPrivate()) {
+ $definition->setPublic($defaults->isPublic());
+ }
$definition->setAutowired($defaults->isAutowired());
$definition->setAutoconfigured($defaults->isAutoconfigured());
- $definition->setBindings($defaults->getBindings());
+ // deep clone, to avoid multiple process of the same instance in the passes
+ $definition->setBindings(unserialize(serialize($defaults->getBindings())));
$definition->setChanges([]);
$this->loader = $loader;
diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServicesConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServicesConfigurator.php
index e7677eb5e6dc6..b6ccbc63b4e81 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServicesConfigurator.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServicesConfigurator.php
@@ -79,10 +79,13 @@ final public function set($id, $class = null)
$allowParent = !$defaults->getChanges() && empty($this->instanceof);
$definition = new Definition();
- $definition->setPublic($defaults->isPublic());
+ if (!$defaults->isPublic() || !$defaults->isPrivate()) {
+ $definition->setPublic($defaults->isPublic() && !$defaults->isPrivate());
+ }
$definition->setAutowired($defaults->isAutowired());
$definition->setAutoconfigured($defaults->isAutoconfigured());
- $definition->setBindings($defaults->getBindings());
+ // deep clone, to avoid multiple process of the same instance in the passes
+ $definition->setBindings(unserialize(serialize($defaults->getBindings())));
$definition->setChanges([]);
$configurator = new ServiceConfigurator($this->container, $this->instanceof, $allowParent, $this, $definition, $id, $defaults->getTags());
@@ -101,7 +104,10 @@ final public function set($id, $class = null)
final public function alias($id, $referencedId)
{
$ref = static::processValue($referencedId, true);
- $alias = new Alias((string) $ref, $this->defaults->isPublic());
+ $alias = new Alias((string) $ref);
+ if (!$this->defaults->isPublic() || !$this->defaults->isPrivate()) {
+ $alias->setPublic($this->defaults->isPublic());
+ }
$this->container->setAlias($id, $alias);
return new AliasConfigurator($this, $alias);
diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
index 5a011c04ddaaf..bc0c55e94df41 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
@@ -355,7 +355,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 (isset($service['public'])) {
$alias->setPublic($service['public']);
} elseif (isset($defaults['public'])) {
$alias->setPublic($defaults['public']);
@@ -660,7 +660,7 @@ protected function loadFile($file)
try {
$configuration = $this->yamlParser->parseFile($file, Yaml::PARSE_CONSTANT | Yaml::PARSE_CUSTOM_TAGS);
} catch (ParseException $e) {
- throw new InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML: %s.', $file, $e->getMessage()), 0, $e);
+ throw new InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML: '.$e->getMessage(), $file), 0, $e);
} finally {
restore_error_handler();
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/basic.expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/basic.expected.yml
index 1137961ade139..39a3b631b97eb 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/basic.expected.yml
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/basic.expected.yml
@@ -6,5 +6,4 @@ services:
synthetic: true
App\BarService:
class: App\BarService
- public: true
arguments: [!service { class: FooClass }]
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/child.expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/child.expected.yml
index aaab7131c4697..f60d6bb5b72e2 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/child.expected.yml
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/child.expected.yml
@@ -6,10 +6,7 @@ services:
synthetic: true
foo:
class: Class2
- public: true
file: file.php
lazy: true
arguments: [!service { class: Class1, public: false }]
- bar:
- alias: foo
- public: true
+ bar: '@foo'
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/defaults.expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/defaults.expected.yml
index a534f7267a078..3f01b1099f73a 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/defaults.expected.yml
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/defaults.expected.yml
@@ -6,7 +6,6 @@ services:
synthetic: true
App\BarService:
class: App\BarService
- public: true
arguments: [!service { class: FooClass }]
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo:
class: Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/instanceof.expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/instanceof.expected.yml
index b12a304221dd8..1238a7bda4fbf 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/instanceof.expected.yml
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/instanceof.expected.yml
@@ -6,7 +6,6 @@ services:
synthetic: true
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo:
class: Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo
- public: true
tags:
- { name: tag, k: v }
lazy: true
@@ -18,4 +17,3 @@ services:
configurator: c
foo:
class: App\FooService
- public: true
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/php7.expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/php7.expected.yml
index 7c5b714ffbd7e..7cec320b2eec7 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/php7.expected.yml
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/php7.expected.yml
@@ -13,7 +13,6 @@ services:
arguments: ['@bar']
bar:
class: Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo
- public: true
calls:
- [setFoo, { }]
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype.expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype.expected.yml
index ebfe087d779cf..24a79401531c9 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype.expected.yml
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype.expected.yml
@@ -6,7 +6,6 @@ services:
synthetic: true
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo:
class: Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo
- public: true
tags:
- { name: foo }
- { name: baz }
@@ -15,7 +14,6 @@ services:
factory: f
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Sub\Bar:
class: Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Sub\Bar
- public: true
tags:
- { name: foo }
- { name: baz }
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services9.php
index d9373a2a6f9e6..ef390937687b1 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services9.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services9.php
@@ -14,7 +14,7 @@
$p->set('foo_class', FooClass::class)
->set('foo', 'bar');
- $s = $c->services();
+ $s = $c->services()->defaults()->public();
$s->set('foo')
->args(['foo', ref('foo.baz'), ['%foo%' => 'foo is %foo%', 'foobar' => '%foo%'], true, ref('service_container')])
->class(FooClass::class)
@@ -120,7 +120,6 @@
->tag('foo');
$s->set('tagged_iterator', 'Bar')
- ->public()
->args([tagged('foo')]);
$s->alias('alias_for_foo', 'foo')->private()->public();
diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php
index f3d1fe44622a1..a8701533cbd38 100644
--- a/src/Symfony/Component/Filesystem/Filesystem.php
+++ b/src/Symfony/Component/Filesystem/Filesystem.php
@@ -101,7 +101,7 @@ public function mkdir($dirs, $mode = 0777)
if (!is_dir($dir)) {
// The directory was not created by a concurrent process. Let's throw an exception with a developer friendly error message if we have one
if (self::$lastError) {
- throw new IOException(sprintf('Failed to create "%s": %s.', $dir, self::$lastError), 0, null, $dir);
+ throw new IOException(sprintf('Failed to create "%s": '.self::$lastError, $dir), 0, null, $dir);
}
throw new IOException(sprintf('Failed to create "%s".', $dir), 0, null, $dir);
}
@@ -171,16 +171,16 @@ public function remove($files)
if (is_link($file)) {
// See https://bugs.php.net/52176
if (!(self::box('unlink', $file) || '\\' !== \DIRECTORY_SEPARATOR || self::box('rmdir', $file)) && file_exists($file)) {
- throw new IOException(sprintf('Failed to remove symlink "%s": %s.', $file, self::$lastError));
+ throw new IOException(sprintf('Failed to remove symlink "%s": '.self::$lastError, $file));
}
} elseif (is_dir($file)) {
$this->remove(new \FilesystemIterator($file, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS));
if (!self::box('rmdir', $file) && file_exists($file)) {
- throw new IOException(sprintf('Failed to remove directory "%s": %s.', $file, self::$lastError));
+ throw new IOException(sprintf('Failed to remove directory "%s": '.self::$lastError, $file));
}
} elseif (!self::box('unlink', $file) && file_exists($file)) {
- throw new IOException(sprintf('Failed to remove file "%s": %s.', $file, self::$lastError));
+ throw new IOException(sprintf('Failed to remove file "%s": '.self::$lastError, $file));
}
}
}
diff --git a/src/Symfony/Component/Filesystem/Tests/LockHandlerTest.php b/src/Symfony/Component/Filesystem/Tests/LockHandlerTest.php
index d130803d44cae..b63e0c224fde8 100644
--- a/src/Symfony/Component/Filesystem/Tests/LockHandlerTest.php
+++ b/src/Symfony/Component/Filesystem/Tests/LockHandlerTest.php
@@ -24,7 +24,7 @@ class LockHandlerTest extends TestCase
public function testConstructWhenRepositoryDoesNotExist()
{
$this->expectException('Symfony\Component\Filesystem\Exception\IOException');
- $this->expectExceptionMessage('Failed to create "/a/b/c/d/e": mkdir(): Permission denied.');
+ $this->expectExceptionMessage('Failed to create "/a/b/c/d/e": mkdir(): Permission denied');
if (!getenv('USER') || 'root' === getenv('USER')) {
$this->markTestSkipped('This test will fail if run under superuser');
}
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php
index 4a7c75b2652a0..fc701d5203dce 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php
@@ -203,6 +203,14 @@ public function buildView(FormView $view, FormInterface $form, array $options)
// * the html5 is set to true
if ($options['html5'] && 'single_text' === $options['widget'] && self::HTML5_FORMAT === $options['format']) {
$view->vars['type'] = 'datetime-local';
+
+ // we need to force the browser to display the seconds by
+ // adding the HTML attribute step if not already defined.
+ // Otherwise the browser will not display and so not send the seconds
+ // therefore the value will always be considered as invalid.
+ if ($options['with_seconds'] && !isset($view->vars['attr']['step'])) {
+ $view->vars['attr']['step'] = 1;
+ }
}
}
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/RepeatedType.php b/src/Symfony/Component/Form/Extension/Core/Type/RepeatedType.php
index ffb7520a582b6..6ed403523cb77 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/RepeatedType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/RepeatedType.php
@@ -31,13 +31,16 @@ public function buildForm(FormBuilderInterface $builder, array $options)
$options['options']['error_bubbling'] = $options['error_bubbling'];
}
+ // children fields must always be mapped
+ $defaultOptions = ['mapped' => true];
+
$builder
->addViewTransformer(new ValueToDuplicatesTransformer([
$options['first_name'],
$options['second_name'],
]))
- ->add($options['first_name'], $options['type'], array_merge($options['options'], $options['first_options']))
- ->add($options['second_name'], $options['type'], array_merge($options['options'], $options['second_options']))
+ ->add($options['first_name'], $options['type'], array_merge($options['options'], $options['first_options'], $defaultOptions))
+ ->add($options['second_name'], $options['type'], array_merge($options['options'], $options['second_options'], $defaultOptions))
;
}
diff --git a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
index 07ecabdad1346..14158f4c1cf74 100644
--- a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
+++ b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
@@ -24,6 +24,8 @@
*/
class FormValidator extends ConstraintValidator
{
+ private $resolvedGroups;
+
/**
* {@inheritdoc}
*/
@@ -44,42 +46,68 @@ public function validate($form, Constraint $formConstraint)
if ($form->isSubmitted() && $form->isSynchronized()) {
// Validate the form data only if transformation succeeded
- $groups = self::getValidationGroups($form);
+ $groups = $this->getValidationGroups($form);
if (!$groups) {
return;
}
$data = $form->getData();
-
// Validate the data against its own constraints
- if ($form->isRoot() && (\is_object($data) || \is_array($data))) {
- if (($groups && \is_array($groups)) || ($groups instanceof GroupSequence && $groups->groups)) {
- $validator->atPath('data')->validate($form->getData(), null, $groups);
- }
- }
+ $validateDataGraph = $form->isRoot()
+ && (\is_object($data) || \is_array($data))
+ && (($groups && \is_array($groups)) || ($groups instanceof GroupSequence && $groups->groups))
+ ;
- // Validate the data against the constraints defined
- // in the form
+ // Validate the data against the constraints defined in the form
+ /** @var Constraint[] $constraints */
$constraints = $config->getOption('constraints', []);
if ($groups instanceof GroupSequence) {
- $validator->atPath('data')->validate($form->getData(), $constraints, $groups);
- // Otherwise validate a constraint only once for the first
- // matching group
- foreach ($groups as $group) {
- if (\in_array($group, $formConstraint->groups)) {
- $validator->atPath('data')->validate($form->getData(), $formConstraint, $group);
- if (\count($this->context->getViolations()) > 0) {
- break;
+ // Validate the data, the form AND nested fields in sequence
+ $violationsCount = $this->context->getViolations()->count();
+ $fieldPropertyPath = \is_object($data) ? 'children[%s]' : 'children%s';
+ $hasChildren = $form->count() > 0;
+ $this->resolvedGroups = $hasChildren ? new \SplObjectStorage() : null;
+
+ foreach ($groups->groups as $group) {
+ if ($validateDataGraph) {
+ $validator->atPath('data')->validate($data, null, $group);
+ }
+
+ if ($groupedConstraints = self::getConstraintsInGroups($constraints, $group)) {
+ $validator->atPath('data')->validate($data, $groupedConstraints, $group);
+ }
+
+ foreach ($form->all() as $field) {
+ if ($field->isSubmitted()) {
+ // remember to validate this field is one group only
+ // otherwise resolving the groups would reuse the same
+ // sequence recursively, thus some fields could fail
+ // in different steps without breaking early enough
+ $this->resolvedGroups[$field] = (array) $group;
+ $validator->atPath(sprintf($fieldPropertyPath, $field->getPropertyPath()))->validate($field, $formConstraint);
}
}
+
+ if ($violationsCount < $this->context->getViolations()->count()) {
+ break;
+ }
+ }
+
+ if ($hasChildren) {
+ // destroy storage at the end of the sequence to avoid memory leaks
+ $this->resolvedGroups = null;
}
} else {
+ if ($validateDataGraph) {
+ $validator->atPath('data')->validate($data, null, $groups);
+ }
+
foreach ($constraints as $constraint) {
// For the "Valid" constraint, validate the data in all groups
if ($constraint instanceof Valid) {
- $validator->atPath('data')->validate($form->getData(), $constraint, $groups);
+ $validator->atPath('data')->validate($data, $constraint, $groups);
continue;
}
@@ -88,7 +116,7 @@ public function validate($form, Constraint $formConstraint)
// matching group
foreach ($groups as $group) {
if (\in_array($group, $constraint->groups)) {
- $validator->atPath('data')->validate($form->getData(), $constraint, $group);
+ $validator->atPath('data')->validate($data, $constraint, $group);
// Prevent duplicate validation
if (!$constraint instanceof Composite) {
@@ -147,7 +175,7 @@ public function validate($form, Constraint $formConstraint)
*
* @return string|GroupSequence|(string|GroupSequence)[] The validation groups
*/
- private static function getValidationGroups(FormInterface $form)
+ private function getValidationGroups(FormInterface $form)
{
// Determine the clicked button of the complete form tree
$clickedButton = null;
@@ -171,6 +199,10 @@ private static function getValidationGroups(FormInterface $form)
return self::resolveValidationGroups($groups, $form);
}
+ if (isset($this->resolvedGroups[$form])) {
+ return $this->resolvedGroups[$form];
+ }
+
$form = $form->getParent();
} while (null !== $form);
@@ -197,4 +229,11 @@ private static function resolveValidationGroups($groups, FormInterface $form)
return (array) $groups;
}
+
+ private static function getConstraintsInGroups($constraints, $group)
+ {
+ return array_filter($constraints, static function (Constraint $constraint) use ($group) {
+ return \in_array($group, $constraint->groups, true);
+ });
+ }
}
diff --git a/src/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php b/src/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php
index 80c94dc66e4d0..e6cd81a4245f5 100644
--- a/src/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php
+++ b/src/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php
@@ -55,8 +55,7 @@ public function validateForm(FormEvent $event)
foreach ($this->validator->validate($form) as $violation) {
// Allow the "invalid" constraint to be put onto
// non-synchronized forms
- // ConstraintViolation::getConstraint() must not expect to provide a constraint as long as Symfony\Component\Validator\ExecutionContext exists (before 3.0)
- $allowNonSynchronized = (null === $violation->getConstraint() || $violation->getConstraint() instanceof Form) && Form::NOT_SYNCHRONIZED_ERROR === $violation->getCode();
+ $allowNonSynchronized = $violation->getConstraint() instanceof Form && Form::NOT_SYNCHRONIZED_ERROR === $violation->getCode();
$this->violationMapper->mapViolation($violation, $form, $allowNonSynchronized);
}
diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php
index 53ac447d3d8bd..e9190b82b8466 100644
--- a/src/Symfony/Component/Form/Form.php
+++ b/src/Symfony/Component/Form/Form.php
@@ -1031,7 +1031,7 @@ private function modelToNorm($value)
$value = $transformer->transform($value);
}
} catch (TransformationFailedException $exception) {
- throw new TransformationFailedException(sprintf('Unable to transform data for property path "%s": %s', $this->getPropertyPath(), $exception->getMessage()), $exception->getCode(), $exception);
+ throw new TransformationFailedException(sprintf('Unable to transform data for property path "%s": '.$exception->getMessage(), $this->getPropertyPath()), $exception->getCode(), $exception);
}
return $value;
@@ -1055,7 +1055,7 @@ private function normToModel($value)
$value = $transformers[$i]->reverseTransform($value);
}
} catch (TransformationFailedException $exception) {
- throw new TransformationFailedException(sprintf('Unable to reverse value for property path "%s": %s', $this->getPropertyPath(), $exception->getMessage()), $exception->getCode(), $exception);
+ throw new TransformationFailedException(sprintf('Unable to reverse value for property path "%s": '.$exception->getMessage(), $this->getPropertyPath()), $exception->getCode(), $exception);
}
return $value;
@@ -1086,7 +1086,7 @@ private function normToView($value)
$value = $transformer->transform($value);
}
} catch (TransformationFailedException $exception) {
- throw new TransformationFailedException(sprintf('Unable to transform value for property path "%s": %s', $this->getPropertyPath(), $exception->getMessage()), $exception->getCode(), $exception);
+ throw new TransformationFailedException(sprintf('Unable to transform value for property path "%s": '.$exception->getMessage(), $this->getPropertyPath()), $exception->getCode(), $exception);
}
return $value;
@@ -1112,7 +1112,7 @@ private function viewToNorm($value)
$value = $transformers[$i]->reverseTransform($value);
}
} catch (TransformationFailedException $exception) {
- throw new TransformationFailedException(sprintf('Unable to reverse value for property path "%s": %s', $this->getPropertyPath(), $exception->getMessage()), $exception->getCode(), $exception);
+ throw new TransformationFailedException(sprintf('Unable to reverse value for property path "%s": '.$exception->getMessage(), $this->getPropertyPath()), $exception->getCode(), $exception);
}
return $value;
diff --git a/src/Symfony/Component/Form/Resources/config/validation.xml b/src/Symfony/Component/Form/Resources/config/validation.xml
index cbd586b915451..b2b935442d467 100644
--- a/src/Symfony/Component/Form/Resources/config/validation.xml
+++ b/src/Symfony/Component/Form/Resources/config/validation.xml
@@ -7,7 +7,7 @@
-
+
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php
index 8af524f1fceae..53edd21b91c5d 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php
@@ -444,6 +444,37 @@ public function testDontPassHtml5TypeIfNotSingleText()
$this->assertArrayNotHasKey('type', $view->vars);
}
+ public function testSingleTextWidgetWithSecondsShouldHaveRightStepAttribute()
+ {
+ $view = $this->factory
+ ->create(static::TESTED_TYPE, null, [
+ 'widget' => 'single_text',
+ 'with_seconds' => true,
+ ])
+ ->createView()
+ ;
+
+ $this->assertArrayHasKey('step', $view->vars['attr']);
+ $this->assertEquals(1, $view->vars['attr']['step']);
+ }
+
+ public function testSingleTextWidgetWithSecondsShouldNotOverrideStepAttribute()
+ {
+ $view = $this->factory
+ ->create(static::TESTED_TYPE, null, [
+ 'widget' => 'single_text',
+ 'with_seconds' => true,
+ 'attr' => [
+ 'step' => 30,
+ ],
+ ])
+ ->createView()
+ ;
+
+ $this->assertArrayHasKey('step', $view->vars['attr']);
+ $this->assertEquals(30, $view->vars['attr']['step']);
+ }
+
public function testDateTypeChoiceErrorsBubbleUp()
{
$error = new FormError('Invalid!');
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php
index 8b4666d6fa5f0..3d5544c05d25a 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Tests\Extension\Core\Type;
use Symfony\Component\Form\Form;
+use Symfony\Component\Form\Tests\Fixtures\NotMappedType;
class RepeatedTypeTest extends BaseTypeTest
{
@@ -78,6 +79,41 @@ public function testSetRequired()
$this->assertFalse($form['second']->isRequired());
}
+ public function testMappedOverridesDefault()
+ {
+ $form = $this->factory->create(NotMappedType::class);
+ $this->assertFalse($form->getConfig()->getMapped());
+
+ $form = $this->factory->create(static::TESTED_TYPE, null, [
+ 'type' => NotMappedType::class,
+ ]);
+
+ $this->assertTrue($form['first']->getConfig()->getMapped());
+ $this->assertTrue($form['second']->getConfig()->getMapped());
+ }
+
+ /**
+ * @dataProvider notMappedConfigurationKeys
+ */
+ public function testNotMappedInnerIsOverridden($configurationKey)
+ {
+ $form = $this->factory->create(static::TESTED_TYPE, null, [
+ 'type' => TextTypeTest::TESTED_TYPE,
+ $configurationKey => ['mapped' => false],
+ ]);
+
+ $this->assertTrue($form['first']->getConfig()->getMapped());
+ $this->assertTrue($form['second']->getConfig()->getMapped());
+ }
+
+ public function notMappedConfigurationKeys()
+ {
+ return [
+ ['first_options'],
+ ['second_options'],
+ ];
+ }
+
public function testSetInvalidOptions()
{
$this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException');
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 e19620e790f7c..5181e4122516e 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php
@@ -401,8 +401,8 @@ public function testHandleGroupSequenceValidationGroups()
$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']));
+ $this->expectValidateAt(0, 'data', $object, 'group1');
+ $this->expectValidateAt(1, 'data', $object, 'group2');
$this->validator->validate($form, new Form());
@@ -756,6 +756,39 @@ public function testCompositeConstraintValidatedInEachGroup()
$this->assertSame('data[field2]', $context->getViolations()[1]->getPropertyPath());
}
+ public function testCompositeConstraintValidatedInSequence()
+ {
+ $form = $this->getCompoundForm([], [
+ 'constraints' => [
+ new Collection([
+ 'field1' => new NotBlank([
+ 'groups' => ['field1'],
+ ]),
+ 'field2' => new NotBlank([
+ 'groups' => ['field2'],
+ ]),
+ ]),
+ ],
+ 'validation_groups' => new GroupSequence(['field1', 'field2']),
+ ])
+ ->add($this->getForm('field1'))
+ ->add($this->getForm('field2'))
+ ;
+
+ $form->submit([
+ 'field1' => '',
+ 'field2' => '',
+ ]);
+
+ $context = new ExecutionContext(Validation::createValidator(), $form, new IdentityTranslator());
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+
+ $this->assertCount(1, $context->getViolations());
+ $this->assertSame('This value should not be blank.', $context->getViolations()[0]->getMessage());
+ $this->assertSame('data[field1]', $context->getViolations()[0]->getPropertyPath());
+ }
+
protected function createValidator()
{
return new FormValidator();
@@ -784,7 +817,7 @@ private function getForm($name = 'name', $dataClass = null, array $options = [])
private function getCompoundForm($data, array $options = [])
{
- return $this->getBuilder('name', \get_class($data), $options)
+ return $this->getBuilder('name', \is_object($data) ? \get_class($data) : null, $options)
->setData($data)
->setCompound(true)
->setDataMapper(new PropertyPathMapper())
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 57f92b6574e3b..4c90cc6316db8 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php
@@ -12,15 +12,19 @@
namespace Symfony\Component\Form\Tests\Extension\Validator\Type;
use Symfony\Component\Form\Extension\Validator\ValidatorExtension;
+use Symfony\Component\Form\Form;
use Symfony\Component\Form\Forms;
use Symfony\Component\Form\Test\Traits\ValidatorExtensionTrait;
use Symfony\Component\Form\Tests\Extension\Core\Type\FormTypeTest;
use Symfony\Component\Form\Tests\Extension\Core\Type\TextTypeTest;
-use Symfony\Component\Validator\Constraints\Email;
+use Symfony\Component\Form\Tests\Fixtures\Author;
use Symfony\Component\Validator\Constraints\GroupSequence;
use Symfony\Component\Validator\Constraints\Length;
+use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Valid;
use Symfony\Component\Validator\ConstraintViolationList;
+use Symfony\Component\Validator\Mapping\ClassMetadata;
+use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
use Symfony\Component\Validator\Validation;
class FormTypeValidatorExtensionTest extends BaseValidatorExtensionTest
@@ -64,14 +68,69 @@ public function testGroupSequenceWithConstraintsOption()
->add('field', TextTypeTest::TESTED_TYPE, [
'constraints' => [
new Length(['min' => 10, 'groups' => ['First']]),
- new Email(['groups' => ['Second']]),
+ new NotBlank(['groups' => ['Second']]),
],
])
;
$form->submit(['field' => 'wrong']);
- $this->assertCount(1, $form->getErrors(true));
+ $errors = $form->getErrors(true);
+
+ $this->assertCount(1, $errors);
+ $this->assertInstanceOf(Length::class, $errors[0]->getCause()->getConstraint());
+ }
+
+ public function testManyFieldsGroupSequenceWithConstraintsOption()
+ {
+ $formMetadata = new ClassMetadata(Form::class);
+ $authorMetadata = (new ClassMetadata(Author::class))
+ ->addPropertyConstraint('firstName', new NotBlank(['groups' => 'Second']))
+ ;
+ $metadataFactory = $this->createMock(MetadataFactoryInterface::class);
+ $metadataFactory->expects($this->any())
+ ->method('getMetadataFor')
+ ->willReturnCallback(static function ($classOrObject) use ($formMetadata, $authorMetadata) {
+ if (Author::class === $classOrObject || $classOrObject instanceof Author) {
+ return $authorMetadata;
+ }
+
+ if (Form::class === $classOrObject || $classOrObject instanceof Form) {
+ return $formMetadata;
+ }
+
+ return new ClassMetadata(\is_string($classOrObject) ? $classOrObject : \get_class($classOrObject));
+ })
+ ;
+
+ $validator = Validation::createValidatorBuilder()
+ ->setMetadataFactory($metadataFactory)
+ ->getValidator()
+ ;
+ $form = Forms::createFormFactoryBuilder()
+ ->addExtension(new ValidatorExtension($validator))
+ ->getFormFactory()
+ ->create(FormTypeTest::TESTED_TYPE, new Author(), (['validation_groups' => new GroupSequence(['First', 'Second'])]))
+ ->add('firstName', TextTypeTest::TESTED_TYPE)
+ ->add('lastName', TextTypeTest::TESTED_TYPE, [
+ 'constraints' => [
+ new Length(['min' => 10, 'groups' => ['First']]),
+ ],
+ ])
+ ->add('australian', TextTypeTest::TESTED_TYPE, [
+ 'constraints' => [
+ new NotBlank(['groups' => ['Second']]),
+ ],
+ ])
+ ;
+
+ $form->submit(['firstName' => '', 'lastName' => 'wrong_1', 'australian' => '']);
+
+ $errors = $form->getErrors(true);
+
+ $this->assertCount(1, $errors);
+ $this->assertInstanceOf(Length::class, $errors[0]->getCause()->getConstraint());
+ $this->assertSame('children[lastName].data', $errors[0]->getCause()->getPropertyPath());
}
protected function createForm(array $options = [])
diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php
index 136086a5e5ba8..cb9b93abdbf61 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php
@@ -13,6 +13,8 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\Extension\Core\Type\FormType;
+use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Validator\Constraints\Form as FormConstraint;
use Symfony\Component\Form\Extension\Validator\ValidatorExtension;
use Symfony\Component\Form\Extension\Validator\ValidatorTypeGuesser;
@@ -20,6 +22,8 @@
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormFactoryBuilder;
use Symfony\Component\OptionsResolver\OptionsResolver;
+use Symfony\Component\Validator\Constraints\GroupSequence;
+use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Mapping\CascadingStrategy;
use Symfony\Component\Validator\Mapping\ClassMetadata;
@@ -49,6 +53,8 @@ public function test2Dot5ValidationApi()
$this->assertCount(1, $metadata->getConstraints());
$this->assertInstanceOf(FormConstraint::class, $metadata->getConstraints()[0]);
+ $this->assertSame(CascadingStrategy::NONE, $metadata->cascadingStrategy);
+ $this->assertSame(TraversalStrategy::IMPLICIT, $metadata->traversalStrategy);
$this->assertSame(CascadingStrategy::CASCADE, $metadata->getPropertyMetadata('children')[0]->cascadingStrategy);
$this->assertSame(TraversalStrategy::IMPLICIT, $metadata->getPropertyMetadata('children')[0]->traversalStrategy);
}
@@ -86,7 +92,53 @@ public function testFieldConstraintsInvalidateFormIfFieldIsSubmitted()
$this->assertFalse($form->get('baz')->isValid());
}
- private function createForm($type)
+ public function testFieldsValidateInSequence()
+ {
+ $form = $this->createForm(FormType::class, null, [
+ 'validation_groups' => new GroupSequence(['group1', 'group2']),
+ ])
+ ->add('foo', TextType::class, [
+ 'constraints' => [new Length(['min' => 10, 'groups' => ['group1']])],
+ ])
+ ->add('bar', TextType::class, [
+ 'constraints' => [new NotBlank(['groups' => ['group2']])],
+ ])
+ ;
+
+ $form->submit(['foo' => 'invalid', 'bar' => null]);
+
+ $errors = $form->getErrors(true);
+
+ $this->assertCount(1, $errors);
+ $this->assertInstanceOf(Length::class, $errors[0]->getCause()->getConstraint());
+ }
+
+ public function testFieldsValidateInSequenceWithNestedGroupsArray()
+ {
+ $form = $this->createForm(FormType::class, null, [
+ 'validation_groups' => new GroupSequence([['group1', 'group2'], 'group3']),
+ ])
+ ->add('foo', TextType::class, [
+ 'constraints' => [new Length(['min' => 10, 'groups' => ['group1']])],
+ ])
+ ->add('bar', TextType::class, [
+ 'constraints' => [new Length(['min' => 10, 'groups' => ['group2']])],
+ ])
+ ->add('baz', TextType::class, [
+ 'constraints' => [new NotBlank(['groups' => ['group3']])],
+ ])
+ ;
+
+ $form->submit(['foo' => 'invalid', 'bar' => 'invalid', 'baz' => null]);
+
+ $errors = $form->getErrors(true);
+
+ $this->assertCount(2, $errors);
+ $this->assertInstanceOf(Length::class, $errors[0]->getCause()->getConstraint());
+ $this->assertInstanceOf(Length::class, $errors[1]->getCause()->getConstraint());
+ }
+
+ private function createForm($type, $data = null, array $options = [])
{
$validator = Validation::createValidatorBuilder()
->setMetadataFactory(new LazyLoadingMetadataFactory(new StaticMethodLoader()))
@@ -95,7 +147,7 @@ private function createForm($type)
$formFactoryBuilder->addExtension(new ValidatorExtension($validator));
$formFactory = $formFactoryBuilder->getFormFactory();
- return $formFactory->create($type);
+ return $formFactory->create($type, $data, $options);
}
}
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/NotMappedType.php b/src/Symfony/Component/Form/Tests/Fixtures/NotMappedType.php
new file mode 100644
index 0000000000000..14c340b8917af
--- /dev/null
+++ b/src/Symfony/Component/Form/Tests/Fixtures/NotMappedType.php
@@ -0,0 +1,23 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests\Fixtures;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+
+class NotMappedType extends AbstractType
+{
+ public function configureOptions(OptionsResolver $resolver)
+ {
+ $resolver->setDefault('mapped', false);
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/AbstractSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/AbstractSessionHandler.php
index ec59d895ce3a3..c80da20466b4a 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/AbstractSessionHandler.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/AbstractSessionHandler.php
@@ -71,6 +71,15 @@ public function validateId($sessionId)
$this->prefetchData = $this->read($sessionId);
$this->prefetchId = $sessionId;
+ if (\PHP_VERSION_ID < 70317 || (70400 <= \PHP_VERSION_ID && \PHP_VERSION_ID < 70405)) {
+ // work around https://bugs.php.net/79413
+ foreach (debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS) as $frame) {
+ if (!isset($frame['class']) && isset($frame['function']) && \in_array($frame['function'], ['session_regenerate_id', 'session_create_id'], true)) {
+ return '' === $this->prefetchData;
+ }
+ }
+ }
+
return '' !== $this->prefetchData;
}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php
index 5165b5d0d882f..1f5a3322e1e87 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php
@@ -213,8 +213,10 @@ public function regenerate($destroy = false, $lifetime = null)
return false;
}
- if (null !== $lifetime) {
+ if (null !== $lifetime && $lifetime != ini_get('session.cookie_lifetime')) {
+ $this->save();
ini_set('session.cookie_lifetime', $lifetime);
+ $this->start();
}
if ($destroy) {
@@ -223,10 +225,6 @@ public function regenerate($destroy = false, $lifetime = null)
$isRegenerated = session_regenerate_id($destroy);
- // The reference to $_SESSION in session bags is lost in PHP7 and we need to re-create it.
- // @see https://bugs.php.net/70013
- $this->loadSession();
-
if (null !== $this->emulateSameSite) {
$originalCookie = SessionUtils::popSessionCookie(session_name(), session_id());
if (null !== $originalCookie) {
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Fixtures/regenerate.expected b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Fixtures/regenerate.expected
index baa5f2f6f5cb0..d825f44f7cb86 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Fixtures/regenerate.expected
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Fixtures/regenerate.expected
@@ -11,6 +11,7 @@ validateId
read
doRead: abc|i:123;
read
+doRead: abc|i:123;
write
doWrite: abc|i:123;
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php
index d2cf324525443..7cfcd223e0323 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php
@@ -123,6 +123,19 @@ public function testRegenerateDestroy()
$this->assertEquals(11, $storage->getBag('attributes')->get('legs'));
}
+ public function testRegenerateWithCustomLifetime()
+ {
+ $storage = $this->getStorage();
+ $storage->start();
+ $id = $storage->getId();
+ $lifetime = 999999;
+ $storage->getBag('attributes')->set('legs', 11);
+ $storage->regenerate(false, $lifetime);
+ $this->assertNotEquals($id, $storage->getId());
+ $this->assertEquals(11, $storage->getBag('attributes')->get('legs'));
+ $this->assertEquals($lifetime, ini_get('session.cookie_lifetime'));
+ }
+
public function testSessionGlobalIsUpToDateAfterIdRegeneration()
{
$storage = $this->getStorage();
diff --git a/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php b/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php
index 4e11ac640bd90..6244fdb9e5eaa 100644
--- a/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php
+++ b/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php
@@ -88,7 +88,7 @@ public function getController(Request $request)
try {
$callable = $this->createController($controller);
} catch (\InvalidArgumentException $e) {
- throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable. %s.', $request->getPathInfo(), $e->getMessage()), 0, $e);
+ throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable: '.$e->getMessage(), $request->getPathInfo()), 0, $e);
}
return $callable;
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php
index 99a9cf23e8398..9c05daa36fe96 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php
@@ -172,7 +172,7 @@ private function sanitizeLogs($logs)
continue;
}
- $message = $log['message'];
+ $message = '_'.$log['message'];
$exception = $log['context']['exception'];
if ($exception instanceof SilencedErrorContext) {
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index 7193c8b935b6d..dc9cc048eb53f 100644
--- a/src/Symfony/Component/HttpKernel/Kernel.php
+++ b/src/Symfony/Component/HttpKernel/Kernel.php
@@ -67,11 +67,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
private $requestStackSize = 0;
private $resetServices = false;
- const VERSION = '3.4.39';
- const VERSION_ID = 30439;
+ const VERSION = '3.4.40';
+ const VERSION_ID = 30440;
const MAJOR_VERSION = 3;
const MINOR_VERSION = 4;
- const RELEASE_VERSION = 39;
+ const RELEASE_VERSION = 40;
const EXTRA_VERSION = '';
const END_OF_MAINTENANCE = '11/2020';
diff --git a/src/Symfony/Component/HttpKernel/Log/Logger.php b/src/Symfony/Component/HttpKernel/Log/Logger.php
index f490293a62e03..e05d8c32ec797 100644
--- a/src/Symfony/Component/HttpKernel/Log/Logger.php
+++ b/src/Symfony/Component/HttpKernel/Log/Logger.php
@@ -77,7 +77,7 @@ public function log($level, $message, array $context = [])
}
$formatter = $this->formatter;
- fwrite($this->handle, $formatter($level, $message, $context));
+ @fwrite($this->handle, $formatter($level, $message, $context));
}
/**
diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php
index b03169de93400..4c4abf5aa4d42 100644
--- a/src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php
@@ -248,7 +248,7 @@ public function getUndefinedControllers()
[
'Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest::bar',
\InvalidArgumentException::class,
- '/.?[cC]ontroller(.*?) for URI "\/" is not callable\.( Expected method(.*) Available methods)?/',
+ '/.?[cC]ontroller(.*?) for URI "\/" is not callable:( Expected method(.*) Available methods)?/',
],
];
}
diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php
index e34427a32e5c4..d2684791fecb4 100644
--- a/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php
@@ -131,10 +131,10 @@ public function getUndefinedControllers()
['foo', 'InvalidArgumentException', 'Unable to find controller "foo".'],
['oof::bar', 'InvalidArgumentException', 'Class "oof" does not exist.'],
['stdClass', 'InvalidArgumentException', 'Unable to find controller "stdClass".'],
- ['Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::staticsAction', 'InvalidArgumentException', 'The controller for URI "/" is not callable. Expected method "staticsAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest", did you mean "staticAction"?'],
- ['Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::privateAction', 'InvalidArgumentException', 'The controller for URI "/" is not callable. Method "privateAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'],
- ['Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::protectedAction', 'InvalidArgumentException', 'The controller for URI "/" is not callable. Method "protectedAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'],
- ['Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::undefinedAction', 'InvalidArgumentException', 'The controller for URI "/" is not callable. Expected method "undefinedAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest". Available methods: "publicAction", "staticAction"'],
+ ['Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::staticsAction', 'InvalidArgumentException', 'The controller for URI "/" is not callable: Expected method "staticsAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest", did you mean "staticAction"?'],
+ ['Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::privateAction', 'InvalidArgumentException', 'The controller for URI "/" is not callable: Method "privateAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'],
+ ['Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::protectedAction', 'InvalidArgumentException', 'The controller for URI "/" is not callable: Method "protectedAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'],
+ ['Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::undefinedAction', 'InvalidArgumentException', 'The controller for URI "/" is not callable: Expected method "undefinedAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest". Available methods: "publicAction", "staticAction"'],
];
}
diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php
index 8b7fbe2a19ea6..b46357ed5585e 100644
--- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php
@@ -132,13 +132,15 @@ public function getCollectTestData()
[
['message' => 'foo3', 'context' => ['exception' => new \ErrorException('warning', 0, E_USER_WARNING)], 'priority' => 100, 'priorityName' => 'DEBUG'],
['message' => 'foo3', 'context' => ['exception' => new SilencedErrorContext(E_USER_WARNING, __FILE__, __LINE__)], 'priority' => 100, 'priorityName' => 'DEBUG'],
+ ['message' => '0', 'context' => ['exception' => new SilencedErrorContext(E_USER_WARNING, __FILE__, __LINE__)], 'priority' => 100, 'priorityName' => 'DEBUG'],
],
[
['message' => 'foo3', 'context' => ['exception' => ['warning', E_USER_WARNING]], 'priority' => 100, 'priorityName' => 'DEBUG'],
['message' => 'foo3', 'context' => ['exception' => [E_USER_WARNING]], 'priority' => 100, 'priorityName' => 'DEBUG', 'errorCount' => 1, 'scream' => true],
+ ['message' => '0', 'context' => ['exception' => [E_USER_WARNING]], 'priority' => 100, 'priorityName' => 'DEBUG', 'errorCount' => 1, 'scream' => true],
],
0,
- 1,
+ 2,
];
}
}
diff --git a/src/Symfony/Component/Intl/Data/Bundle/Reader/JsonBundleReader.php b/src/Symfony/Component/Intl/Data/Bundle/Reader/JsonBundleReader.php
index a56fd6af1c233..555dd377c9341 100644
--- a/src/Symfony/Component/Intl/Data/Bundle/Reader/JsonBundleReader.php
+++ b/src/Symfony/Component/Intl/Data/Bundle/Reader/JsonBundleReader.php
@@ -46,7 +46,7 @@ public function read($path, $locale)
$data = json_decode(file_get_contents($fileName), true);
if (null === $data) {
- throw new RuntimeException(sprintf('The resource bundle "%s" contains invalid JSON: %s.', $fileName, json_last_error_msg()));
+ throw new RuntimeException(sprintf('The resource bundle "%s" contains invalid JSON: '.json_last_error_msg(), $fileName));
}
return $data;
diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Collection.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Collection.php
index fade430753dcc..f166c3dd3fb9c 100644
--- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Collection.php
+++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Collection.php
@@ -48,7 +48,7 @@ public function count()
return $count;
}
- throw new LdapException(sprintf('Error while retrieving entry count: %s.', ldap_error($this->connection->getResource())));
+ throw new LdapException('Error while retrieving entry count: '.ldap_error($this->connection->getResource()));
}
public function getIterator()
@@ -62,7 +62,7 @@ public function getIterator()
}
if (false === $current) {
- throw new LdapException(sprintf('Could not rewind entries array: %s.', ldap_error($con)));
+ throw new LdapException('Could not rewind entries array: '.ldap_error($con));
}
yield $this->getSingleEntry($con, $current);
@@ -105,7 +105,7 @@ private function getSingleEntry($con, $current)
$attributes = ldap_get_attributes($con, $current);
if (false === $attributes) {
- throw new LdapException(sprintf('Could not fetch attributes: %s.', ldap_error($con)));
+ throw new LdapException('Could not fetch attributes: '.ldap_error($con));
}
$attributes = $this->cleanupAttributes($attributes);
@@ -113,7 +113,7 @@ private function getSingleEntry($con, $current)
$dn = ldap_get_dn($con, $current);
if (false === $dn) {
- throw new LdapException(sprintf('Could not fetch DN: %s.', ldap_error($con)));
+ throw new LdapException('Could not fetch DN: '.ldap_error($con));
}
return new Entry($dn, $attributes);
diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php
index 2dcb215b921a4..9e988a8e6c87f 100644
--- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php
+++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php
@@ -138,11 +138,11 @@ private function connect()
}
if (false === $this->connection) {
- throw new LdapException(sprintf('Could not connect to Ldap server: %s.', ldap_error($this->connection)));
+ throw new LdapException('Could not connect to Ldap server: '.ldap_error($this->connection));
}
if ('tls' === $this->config['encryption'] && false === @ldap_start_tls($this->connection)) {
- throw new LdapException(sprintf('Could not initiate TLS connection: %s.', ldap_error($this->connection)));
+ throw new LdapException('Could not initiate TLS connection: '.ldap_error($this->connection));
}
}
diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php
index 871c4b049562d..789672e7b22ef 100644
--- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php
+++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php
@@ -38,7 +38,7 @@ public function add(Entry $entry)
$con = $this->getConnectionResource();
if (!@ldap_add($con, $entry->getDn(), $entry->getAttributes())) {
- throw new LdapException(sprintf('Could not add entry "%s": %s.', $entry->getDn(), ldap_error($con)));
+ throw new LdapException(sprintf('Could not add entry "%s": '.ldap_error($con), $entry->getDn()));
}
return $this;
@@ -52,7 +52,7 @@ public function update(Entry $entry)
$con = $this->getConnectionResource();
if (!@ldap_modify($con, $entry->getDn(), $entry->getAttributes())) {
- throw new LdapException(sprintf('Could not update entry "%s": %s.', $entry->getDn(), ldap_error($con)));
+ throw new LdapException(sprintf('Could not update entry "%s": '.ldap_error($con), $entry->getDn()));
}
}
@@ -64,7 +64,7 @@ public function remove(Entry $entry)
$con = $this->getConnectionResource();
if (!@ldap_delete($con, $entry->getDn())) {
- throw new LdapException(sprintf('Could not remove entry "%s": %s.', $entry->getDn(), ldap_error($con)));
+ throw new LdapException(sprintf('Could not remove entry "%s": '.ldap_error($con), $entry->getDn()));
}
}
@@ -76,7 +76,7 @@ public function rename(Entry $entry, $newRdn, $removeOldRdn = true)
$con = $this->getConnectionResource();
if (!@ldap_rename($con, $entry->getDn(), $newRdn, null, $removeOldRdn)) {
- throw new LdapException(sprintf('Could not rename entry "%s" to "%s": %s.', $entry->getDn(), $newRdn, ldap_error($con)));
+ throw new LdapException(sprintf('Could not rename entry "%s" to "%s": '.ldap_error($con), $entry->getDn(), $newRdn));
}
}
diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php
index 33742039fb32b..a5c049b166125 100644
--- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php
+++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php
@@ -45,7 +45,7 @@ public function __destruct()
$this->search = null;
if (!$success) {
- throw new LdapException(sprintf('Could not free results: %s.', ldap_error($con)));
+ throw new LdapException('Could not free results: '.ldap_error($con));
}
}
diff --git a/src/Symfony/Component/OptionsResolver/Tests/Debug/OptionsResolverIntrospectorTest.php b/src/Symfony/Component/OptionsResolver/Tests/Debug/OptionsResolverIntrospectorTest.php
index 8d6e9f63f6370..5fdb9e2657f3b 100644
--- a/src/Symfony/Component/OptionsResolver/Tests/Debug/OptionsResolverIntrospectorTest.php
+++ b/src/Symfony/Component/OptionsResolver/Tests/Debug/OptionsResolverIntrospectorTest.php
@@ -44,7 +44,7 @@ public function testGetDefaultThrowsOnNoConfiguredValue()
$resolver->setDefined($option = 'foo');
$debug = new OptionsResolverIntrospector($resolver);
- $this->assertSame('bar', $debug->getDefault($option));
+ $debug->getDefault($option);
}
public function testGetDefaultThrowsOnNotDefinedOption()
@@ -54,7 +54,7 @@ public function testGetDefaultThrowsOnNotDefinedOption()
$resolver = new OptionsResolver();
$debug = new OptionsResolverIntrospector($resolver);
- $this->assertSame('bar', $debug->getDefault('foo'));
+ $debug->getDefault('foo');
}
public function testGetLazyClosures()
@@ -75,7 +75,7 @@ public function testGetLazyClosuresThrowsOnNoConfiguredValue()
$resolver->setDefined($option = 'foo');
$debug = new OptionsResolverIntrospector($resolver);
- $this->assertSame('bar', $debug->getLazyClosures($option));
+ $debug->getLazyClosures($option);
}
public function testGetLazyClosuresThrowsOnNotDefinedOption()
@@ -85,7 +85,7 @@ public function testGetLazyClosuresThrowsOnNotDefinedOption()
$resolver = new OptionsResolver();
$debug = new OptionsResolverIntrospector($resolver);
- $this->assertSame('bar', $debug->getLazyClosures('foo'));
+ $debug->getLazyClosures('foo');
}
public function testGetAllowedTypes()
diff --git a/src/Symfony/Component/Process/Pipes/WindowsPipes.php b/src/Symfony/Component/Process/Pipes/WindowsPipes.php
index 44056d54cf65d..302b0509a86b7 100644
--- a/src/Symfony/Component/Process/Pipes/WindowsPipes.php
+++ b/src/Symfony/Component/Process/Pipes/WindowsPipes.php
@@ -57,7 +57,7 @@ public function __construct($input, $haveReadSupport)
if (!$h = fopen($file.'.lock', 'w')) {
restore_error_handler();
- throw new RuntimeException(sprintf('A temporary file could not be opened to write the process output: %s.', $lastError));
+ throw new RuntimeException('A temporary file could not be opened to write the process output: '.$lastError);
}
if (!flock($h, LOCK_EX | LOCK_NB)) {
continue 2;
diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php
index 66b38eaf3caef..6f715d620418c 100644
--- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php
+++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php
@@ -472,7 +472,7 @@ private function readProperty($zval, $property)
} catch (\TypeError $e) {
// handle uninitialized properties in PHP >= 7
if (preg_match((sprintf('/^Return value of %s::%s\(\) must be of the type (\w+), null returned$/', preg_quote(\get_class($object)), $access[self::ACCESS_NAME])), $e->getMessage(), $matches)) {
- throw new AccessException(sprintf('The method "%s::%s()" returned "null", but expected type "%3$s". Have you forgotten to initialize a property or to make the return type nullable using "?%3$s" instead?', \get_class($object), $access[self::ACCESS_NAME], $matches[1]), 0, $e);
+ throw new AccessException(sprintf('The method "%s::%s()" returned "null", but expected type "%3$s". Did you forget to initialize a property or to make the return type nullable using "?%3$s"?', \get_class($object), $access[self::ACCESS_NAME], $matches[1]), 0, $e);
}
throw $e;
@@ -505,7 +505,7 @@ private function readProperty($zval, $property)
if (\PHP_VERSION_ID >= 70400 && preg_match('/^Typed property ([\w\\\]+)::\$(\w+) must not be accessed before initialization$/', $e->getMessage(), $matches)) {
$r = new \ReflectionProperty($matches[1], $matches[2]);
- throw new AccessException(sprintf('The property "%s::$%s" is not readable because it is typed "%3$s". You should either initialize it or make it nullable using "?%3$s" instead.', $r->getDeclaringClass()->getName(), $r->getName(), $r->getType()->getName()), 0, $e);
+ throw new AccessException(sprintf('The property "%s::$%s" is not readable because it is typed "%s". You should initialize it or declare a default value instead.', $r->getDeclaringClass()->getName(), $r->getName(), $r->getType()->getName()), 0, $e);
}
throw $e;
diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php
index 62a95d68e9304..d8331e76ad811 100644
--- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php
+++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php
@@ -126,7 +126,7 @@ public function testGetValueThrowsExceptionIfIndexNotFoundAndIndexExceptionsEnab
public function testGetValueThrowsExceptionIfUninitializedProperty()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\AccessException');
- $this->expectExceptionMessage('The property "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedProperty::$uninitialized" is not readable because it is typed "string". You should either initialize it or make it nullable using "?string" instead.');
+ $this->expectExceptionMessage('The property "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedProperty::$uninitialized" is not readable because it is typed "string". You should initialize it or declare a default value instead.');
$this->propertyAccessor->getValue(new UninitializedProperty(), 'uninitialized');
}
@@ -137,7 +137,7 @@ public function testGetValueThrowsExceptionIfUninitializedProperty()
public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetter()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\AccessException');
- $this->expectExceptionMessage('The method "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty::getUninitialized()" returned "null", but expected type "array". Have you forgotten to initialize a property or to make the return type nullable using "?array" instead?');
+ $this->expectExceptionMessage('The method "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty::getUninitialized()" returned "null", but expected type "array". Did you forget to initialize a property or to make the return type nullable using "?array"?');
$this->propertyAccessor->getValue(new UninitializedPrivateProperty(), 'uninitialized');
}
diff --git a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
index dcb06ee192249..3306946a9b337 100644
--- a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
+++ b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
@@ -61,6 +61,9 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
*/
private $arrayMutatorPrefixes;
+ private $arrayMutatorPrefixesFirst;
+ private $arrayMutatorPrefixesLast;
+
/**
* @param string[]|null $mutatorPrefixes
* @param string[]|null $accessorPrefixes
@@ -72,6 +75,9 @@ public function __construct(array $mutatorPrefixes = null, array $accessorPrefix
$this->mutatorPrefixes = null !== $mutatorPrefixes ? $mutatorPrefixes : self::$defaultMutatorPrefixes;
$this->accessorPrefixes = null !== $accessorPrefixes ? $accessorPrefixes : self::$defaultAccessorPrefixes;
$this->arrayMutatorPrefixes = null !== $arrayMutatorPrefixes ? $arrayMutatorPrefixes : self::$defaultArrayMutatorPrefixes;
+
+ $this->arrayMutatorPrefixesFirst = array_merge($this->arrayMutatorPrefixes, array_diff($this->mutatorPrefixes, $this->arrayMutatorPrefixes));
+ $this->arrayMutatorPrefixesLast = array_reverse($this->arrayMutatorPrefixesFirst);
}
/**
@@ -330,7 +336,9 @@ private function getMutatorMethod($class, $property)
$ucProperty = ucfirst($property);
$ucSingulars = (array) Inflector::singularize($ucProperty);
- foreach ($this->mutatorPrefixes as $prefix) {
+ $mutatorPrefixes = \in_array($ucProperty, $ucSingulars, true) ? $this->arrayMutatorPrefixesLast : $this->arrayMutatorPrefixesFirst;
+
+ foreach ($mutatorPrefixes as $prefix) {
$names = [$ucProperty];
if (\in_array($prefix, $this->arrayMutatorPrefixes)) {
$names = array_merge($names, $ucSingulars);
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php
index f4f9e9dc77e29..4c19a86b43767 100644
--- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php
+++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
-namespace Symfony\Component\PropertyInfo\Tests\PhpDocExtractor;
+namespace Symfony\Component\PropertyInfo\Tests\Extractor;
use PHPUnit\Framework\TestCase;
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php
index 5a5c0c7b62c6a..36fc9d55e37f8 100644
--- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php
+++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php
@@ -61,6 +61,7 @@ public function testGetProperties()
'realParent',
'xTotals',
'YT',
+ 'date',
'c',
'd',
'e',
@@ -96,6 +97,7 @@ public function testGetPropertiesWithCustomPrefixes()
'foo4',
'foo5',
'files',
+ 'date',
'c',
'd',
'e',
@@ -156,6 +158,8 @@ public function typesProvider()
['staticSetter', null],
['self', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy')]],
['realParent', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')]],
+ ['date', [new Type(Type::BUILTIN_TYPE_OBJECT, false, \DateTime::class)]],
+ ['dates', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, \DateTime::class))]],
];
}
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php
index 663fef84c7760..4a7b7c0951d46 100644
--- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php
+++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php
@@ -190,4 +190,12 @@ public function getXTotals()
public function getYT()
{
}
+
+ public function setDate(\DateTime $date)
+ {
+ }
+
+ public function addDate(\DateTime $date)
+ {
+ }
}
diff --git a/src/Symfony/Component/Routing/Loader/ObjectRouteLoader.php b/src/Symfony/Component/Routing/Loader/ObjectRouteLoader.php
index 3920fc4bef9f6..65633ca075ba4 100644
--- a/src/Symfony/Component/Routing/Loader/ObjectRouteLoader.php
+++ b/src/Symfony/Component/Routing/Loader/ObjectRouteLoader.php
@@ -67,7 +67,7 @@ public function load($resource, $type = null)
if (!$routeCollection instanceof RouteCollection) {
$type = \is_object($routeCollection) ? \get_class($routeCollection) : \gettype($routeCollection);
- throw new \LogicException(sprintf('The "%s"::%s method must return a RouteCollection: "%s" returned.', \get_class($loaderObject), $method, $type));
+ throw new \LogicException(sprintf('The "%s::%s()" method must return a RouteCollection: "%s" returned.', \get_class($loaderObject), $method, $type));
}
// make the service file tracked so that if it changes, the cache rebuilds
diff --git a/src/Symfony/Component/Routing/Loader/XmlFileLoader.php b/src/Symfony/Component/Routing/Loader/XmlFileLoader.php
index 29dfdb1665b56..a9a9d09e0866b 100644
--- a/src/Symfony/Component/Routing/Loader/XmlFileLoader.php
+++ b/src/Symfony/Component/Routing/Loader/XmlFileLoader.php
@@ -240,9 +240,9 @@ private function parseConfigs(\DOMElement $node, $path)
if ($controller = $node->getAttribute('controller')) {
if (isset($defaults['_controller'])) {
- $name = $node->hasAttribute('id') ? sprintf('"%s"', $node->getAttribute('id')) : sprintf('the "%s" tag', $node->tagName);
+ $name = $node->hasAttribute('id') ? sprintf('"%s".', $node->getAttribute('id')) : sprintf('the "%s" tag.', $node->tagName);
- throw new \InvalidArgumentException(sprintf('The routing file "%s" must not specify both the "controller" attribute and the defaults key "_controller" for %s.', $path, $name));
+ throw new \InvalidArgumentException(sprintf('The routing file "%s" must not specify both the "controller" attribute and the defaults key "_controller" for ', $path).$name);
}
$defaults['_controller'] = $controller;
diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php
index 00092c431373c..e68649801b1cc 100644
--- a/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php
+++ b/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php
@@ -87,9 +87,8 @@ protected function checkAuthentication(UserInterface $user, UsernamePasswordToke
}
try {
- $username = $this->ldap->escape($username, '', LdapInterface::ESCAPE_DN);
-
if ($this->queryString) {
+ $username = $this->ldap->escape($username, '', LdapInterface::ESCAPE_FILTER);
$query = str_replace('{username}', $username, $this->queryString);
$result = $this->ldap->query($this->dnString, $query)->execute();
if (1 !== $result->count()) {
@@ -98,6 +97,7 @@ protected function checkAuthentication(UserInterface $user, UsernamePasswordToke
$dn = $result[0]->getDn();
} else {
+ $username = $this->ldap->escape($username, '', LdapInterface::ESCAPE_DN);
$dn = str_replace('{username}', $username, $this->dnString);
}
diff --git a/src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php b/src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php
index f79eed1961fd5..feee0fe856a6d 100644
--- a/src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php
+++ b/src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php
@@ -71,10 +71,10 @@ private function createEncoder(array $config)
$config = $this->getEncoderConfigFromAlgorithm($config);
}
if (!isset($config['class'])) {
- throw new \InvalidArgumentException(sprintf('"class" must be set in %s.', json_encode($config)));
+ throw new \InvalidArgumentException('"class" must be set in '.json_encode($config));
}
if (!isset($config['arguments'])) {
- throw new \InvalidArgumentException(sprintf('"arguments" must be set in %s.', json_encode($config)));
+ throw new \InvalidArgumentException('"arguments" must be set in '.json_encode($config));
}
$reflection = new \ReflectionClass($config['class']);
diff --git a/src/Symfony/Component/Security/Http/Logout/CookieClearingLogoutHandler.php b/src/Symfony/Component/Security/Http/Logout/CookieClearingLogoutHandler.php
index 2aa7c732b976a..9367a62b33bdf 100644
--- a/src/Symfony/Component/Security/Http/Logout/CookieClearingLogoutHandler.php
+++ b/src/Symfony/Component/Security/Http/Logout/CookieClearingLogoutHandler.php
@@ -38,7 +38,7 @@ public function __construct(array $cookies)
public function logout(Request $request, Response $response, TokenInterface $token)
{
foreach ($this->cookies as $cookieName => $cookieData) {
- $response->headers->clearCookie($cookieName, $cookieData['path'], $cookieData['domain']);
+ $response->headers->clearCookie($cookieName, $cookieData['path'], $cookieData['domain'], isset($cookieData['secure']) ? $cookieData['secure'] : false, true, isset($cookieData['samesite']) ? $cookieData['samesite'] : null);
}
}
}
diff --git a/src/Symfony/Component/Security/Http/Tests/Logout/CookieClearingLogoutHandlerTest.php b/src/Symfony/Component/Security/Http/Tests/Logout/CookieClearingLogoutHandlerTest.php
index 8dcc10338b768..f2407fcb3fdd5 100644
--- a/src/Symfony/Component/Security/Http/Tests/Logout/CookieClearingLogoutHandlerTest.php
+++ b/src/Symfony/Component/Security/Http/Tests/Logout/CookieClearingLogoutHandlerTest.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Security\Http\Tests\Logout;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
@@ -25,7 +26,7 @@ public function testLogout()
$response = new Response();
$token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock();
- $handler = new CookieClearingLogoutHandler(['foo' => ['path' => '/foo', 'domain' => 'foo.foo'], 'foo2' => ['path' => null, 'domain' => null]]);
+ $handler = new CookieClearingLogoutHandler(['foo' => ['path' => '/foo', 'domain' => 'foo.foo', 'secure' => true, 'samesite' => Cookie::SAMESITE_STRICT], 'foo2' => ['path' => null, 'domain' => null]]);
$cookies = $response->headers->getCookies();
$this->assertCount(0, $cookies);
@@ -39,12 +40,16 @@ public function testLogout()
$this->assertEquals('foo', $cookie->getName());
$this->assertEquals('/foo', $cookie->getPath());
$this->assertEquals('foo.foo', $cookie->getDomain());
+ $this->assertEquals(Cookie::SAMESITE_STRICT, $cookie->getSameSite());
+ $this->assertTrue($cookie->isSecure());
$this->assertTrue($cookie->isCleared());
$cookie = $cookies['']['/']['foo2'];
$this->assertStringStartsWith('foo2', $cookie->getName());
$this->assertEquals('/', $cookie->getPath());
$this->assertNull($cookie->getDomain());
+ $this->assertNull($cookie->getSameSite());
+ $this->assertFalse($cookie->isSecure());
$this->assertTrue($cookie->isCleared());
}
}
diff --git a/src/Symfony/Component/Security/Http/composer.json b/src/Symfony/Component/Security/Http/composer.json
index 9ede332facfa0..449e07ed8f680 100644
--- a/src/Symfony/Component/Security/Http/composer.json
+++ b/src/Symfony/Component/Security/Http/composer.json
@@ -19,7 +19,7 @@
"php": "^5.5.9|>=7.0.8",
"symfony/security-core": "~3.2|~4.0",
"symfony/event-dispatcher": "~2.8|~3.0|~4.0",
- "symfony/http-foundation": "~2.8|~3.0|~4.0",
+ "symfony/http-foundation": "~3.4.40|^4.4.7",
"symfony/http-kernel": "~3.3|~4.0",
"symfony/polyfill-php56": "~1.0",
"symfony/polyfill-php70": "~1.0",
diff --git a/src/Symfony/Component/Security/composer.json b/src/Symfony/Component/Security/composer.json
index 5d50f82021292..44eedcc9de12e 100644
--- a/src/Symfony/Component/Security/composer.json
+++ b/src/Symfony/Component/Security/composer.json
@@ -18,7 +18,7 @@
"require": {
"php": "^5.5.9|>=7.0.8",
"symfony/event-dispatcher": "~2.8|~3.0|~4.0",
- "symfony/http-foundation": "^2.8.31|~3.3.13|~3.4|~4.0",
+ "symfony/http-foundation": "~3.4.40|^4.4.7",
"symfony/http-kernel": "~3.3|~4.0",
"symfony/polyfill-php56": "~1.0",
"symfony/polyfill-php70": "~1.0",
diff --git a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php
index d7551183ddff2..dc932222e953e 100644
--- a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php
+++ b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php
@@ -431,7 +431,7 @@ private function buildXml(\DOMNode $parentNode, $data, $xmlRootNodeName = null)
return $this->appendNode($parentNode, $data, 'data');
}
- throw new NotEncodableValueException(sprintf('An unexpected value could not be serialized: %s.', !\is_resource($data) ? var_export($data, true) : sprintf('%s resource', get_resource_type($data))));
+ throw new NotEncodableValueException('An unexpected value could not be serialized: '.(!\is_resource($data) ? var_export($data, true) : sprintf('%s resource', get_resource_type($data))));
}
/**
diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php
index 56dc3c590ede3..c1fc4ad82f93a 100644
--- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php
+++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php
@@ -304,7 +304,7 @@ private function validateAndDenormalize($currentClass, $attribute, $data, $forma
*/
protected function denormalizeParameter(\ReflectionClass $class, \ReflectionParameter $parameter, $parameterName, $parameterData, array $context, $format = null)
{
- if (null === $this->propertyTypeExtractor || null === $types = $this->propertyTypeExtractor->getTypes($class->getName(), $parameterName)) {
+ if (null === $this->propertyTypeExtractor || null === $this->propertyTypeExtractor->getTypes($class->getName(), $parameterName)) {
return parent::denormalizeParameter($class, $parameter, $parameterName, $parameterData, $context, $format);
}
@@ -400,6 +400,7 @@ protected function createChildContext(array $parentContext, $attribute/*, string
*/
private function getCacheKey($format, array $context)
{
+ unset($context[self::OBJECT_TO_POPULATE]);
unset($context['cache_key']); // avoid artificially different keys
try {
return md5($format.serialize([
diff --git a/src/Symfony/Component/Serializer/Serializer.php b/src/Symfony/Component/Serializer/Serializer.php
index 1c125bc935df4..87b32a6ca5ba4 100644
--- a/src/Symfony/Component/Serializer/Serializer.php
+++ b/src/Symfony/Component/Serializer/Serializer.php
@@ -164,7 +164,7 @@ public function normalize($data, $format = null, array $context = [])
throw new NotNormalizableValueException(sprintf('Could not normalize object of type "%s", no supporting normalizer found.', \get_class($data)));
}
- throw new NotNormalizableValueException(sprintf('An unexpected value could not be normalized: %s.', !\is_resource($data) ? var_export($data, true) : sprintf('%s resource', get_resource_type($data))));
+ throw new NotNormalizableValueException('An unexpected value could not be normalized: '.(!\is_resource($data) ? var_export($data, true) : sprintf('%s resource', get_resource_type($data))));
}
/**
diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php
index d5d7cace362e4..d269da27e810d 100644
--- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php
@@ -163,6 +163,14 @@ public function testDenormalizeStringCollectionDecodedFromXmlWithTwoChildren()
$this->assertEquals('bar', $stringCollection->children[1]);
}
+ public function testDenormalizeNotSerializableObjectToPopulate()
+ {
+ $normalizer = new AbstractObjectNormalizerDummy();
+ $normalizedData = $normalizer->denormalize(['foo' => 'foo'], Dummy::class, null, [AbstractObjectNormalizer::OBJECT_TO_POPULATE => new NotSerializable()]);
+
+ $this->assertSame('foo', $normalizedData->foo);
+ }
+
private function getDenormalizerForStringCollection()
{
$extractor = $this->getMockBuilder(PhpDocExtractor::class)->getMock();
@@ -379,3 +387,15 @@ public function setSerializer(SerializerInterface $serializer)
$this->serializer = $serializer;
}
}
+
+class NotSerializable
+{
+ public function __sleep()
+ {
+ if (class_exists(\Error::class)) {
+ throw new \Error('not serializable');
+ }
+
+ throw new \Exception('not serializable');
+ }
+}
diff --git a/src/Symfony/Component/Translation/Loader/JsonFileLoader.php b/src/Symfony/Component/Translation/Loader/JsonFileLoader.php
index 5ab2e989e7a76..a02732e8d8f40 100644
--- a/src/Symfony/Component/Translation/Loader/JsonFileLoader.php
+++ b/src/Symfony/Component/Translation/Loader/JsonFileLoader.php
@@ -30,7 +30,7 @@ protected function loadResource($resource)
$messages = json_decode($data, true);
if (0 < $errorCode = json_last_error()) {
- throw new InvalidResourceException(sprintf('Error parsing JSON - %s.', $this->getJSONErrorMessage($errorCode)));
+ throw new InvalidResourceException('Error parsing JSON: '.$this->getJSONErrorMessage($errorCode));
}
}
diff --git a/src/Symfony/Component/Translation/Loader/XliffFileLoader.php b/src/Symfony/Component/Translation/Loader/XliffFileLoader.php
index b5333f1e4e36b..d09f434985107 100644
--- a/src/Symfony/Component/Translation/Loader/XliffFileLoader.php
+++ b/src/Symfony/Component/Translation/Loader/XliffFileLoader.php
@@ -53,7 +53,7 @@ private function extract($resource, MessageCatalogue $catalogue, $domain)
try {
$dom = XmlUtils::loadFile($resource);
} catch (\InvalidArgumentException $e) {
- throw new InvalidResourceException(sprintf('Unable to load "%s": %s.', $resource, $e->getMessage()), $e->getCode(), $e);
+ throw new InvalidResourceException(sprintf('Unable to load "%s": '.$e->getMessage(), $resource), $e->getCode(), $e);
}
$xliffVersion = $this->getVersionNumber($dom);
@@ -194,7 +194,7 @@ private function validateSchema($file, \DOMDocument $dom, $schema)
if (!@$dom->schemaValidateSource($schema)) {
libxml_disable_entity_loader($disableEntities);
- throw new InvalidResourceException(sprintf('Invalid resource provided: "%s"; Errors: %s.', $file, implode("\n", $this->getXmlErrors($internalErrors))));
+ throw new InvalidResourceException(sprintf('Invalid resource provided: "%s"; Errors: '.implode("\n", $this->getXmlErrors($internalErrors)), $file));
}
libxml_disable_entity_loader($disableEntities);
diff --git a/src/Symfony/Component/Translation/Tests/Loader/JsonFileLoaderTest.php b/src/Symfony/Component/Translation/Tests/Loader/JsonFileLoaderTest.php
index d264bb16b29d9..c5a9ca64d4d7f 100644
--- a/src/Symfony/Component/Translation/Tests/Loader/JsonFileLoaderTest.php
+++ b/src/Symfony/Component/Translation/Tests/Loader/JsonFileLoaderTest.php
@@ -50,7 +50,7 @@ public function testLoadNonExistingResource()
public function testParseException()
{
$this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
- $this->expectExceptionMessage('Error parsing JSON - Syntax error, malformed JSON');
+ $this->expectExceptionMessage('Error parsing JSON: Syntax error, malformed JSON');
$loader = new JsonFileLoader();
$resource = __DIR__.'/../fixtures/malformed.json';
$loader->load($resource, 'en', 'domain1');
diff --git a/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php b/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php
index c0840845480dd..10db1586155f6 100644
--- a/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php
+++ b/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php
@@ -55,7 +55,7 @@ public function validate($value, Constraint $constraint)
try {
$comparedValue = $this->getPropertyAccessor()->getValue($object, $path);
} catch (NoSuchPropertyException $e) {
- throw new ConstraintDefinitionException(sprintf('Invalid property path "%s" provided to "%s" constraint: %s.', $path, \get_class($constraint), $e->getMessage()), 0, $e);
+ throw new ConstraintDefinitionException(sprintf('Invalid property path "%s" provided to "%s" constraint: '.$e->getMessage(), $path, \get_class($constraint)), 0, $e);
}
} else {
$comparedValue = $constraint->value;
diff --git a/src/Symfony/Component/Validator/Constraints/CallbackValidator.php b/src/Symfony/Component/Validator/Constraints/CallbackValidator.php
index 84f2c3295ceae..3accd2f9d6158 100644
--- a/src/Symfony/Component/Validator/Constraints/CallbackValidator.php
+++ b/src/Symfony/Component/Validator/Constraints/CallbackValidator.php
@@ -40,7 +40,7 @@ public function validate($object, Constraint $constraint)
if (isset($method[0]) && \is_object($method[0])) {
$method[0] = \get_class($method[0]);
}
- throw new ConstraintDefinitionException(sprintf('%s targeted by Callback constraint is not a valid callable.', json_encode($method)));
+ throw new ConstraintDefinitionException(json_encode($method).' targeted by Callback constraint is not a valid callable.');
}
\call_user_func($method, $object, $this->context, $constraint->payload);
diff --git a/src/Symfony/Component/Validator/Constraints/Composite.php b/src/Symfony/Component/Validator/Constraints/Composite.php
index bd7030ee27a44..b14276c3bbf1a 100644
--- a/src/Symfony/Component/Validator/Constraints/Composite.php
+++ b/src/Symfony/Component/Validator/Constraints/Composite.php
@@ -88,7 +88,8 @@ public function __construct($options = null)
}
}
- $this->groups = array_keys($mergedGroups);
+ // prevent empty composite constraint to have empty groups
+ $this->groups = array_keys($mergedGroups) ?: [self::DEFAULT_GROUP];
$this->$compositeOption = $nestedConstraints;
return;
diff --git a/src/Symfony/Component/Validator/Constraints/UrlValidator.php b/src/Symfony/Component/Validator/Constraints/UrlValidator.php
index d934162eeefc3..97a05450754ea 100644
--- a/src/Symfony/Component/Validator/Constraints/UrlValidator.php
+++ b/src/Symfony/Component/Validator/Constraints/UrlValidator.php
@@ -23,7 +23,7 @@ class UrlValidator extends ConstraintValidator
{
const PATTERN = '~^
(%s):// # protocol
- (([\_\.\pL\pN-]+:)?([\_\.\pL\pN-]+)@)? # basic auth
+ (((?:[\_\.\pL\pN-]|%%[0-9A-Fa-f]{2})+:)?((?:[\_\.\pL\pN-]|%%[0-9A-Fa-f]{2})+)@)? # basic auth
(
([\pL\pN\pS\-\_\.])+(\.?([\pL\pN]|xn\-\-[\pL\pN-]+)+\.?) # a domain name
| # or
diff --git a/src/Symfony/Component/Validator/Context/ExecutionContext.php b/src/Symfony/Component/Validator/Context/ExecutionContext.php
index 7ca9caa80fefb..44427c8489cca 100644
--- a/src/Symfony/Component/Validator/Context/ExecutionContext.php
+++ b/src/Symfony/Component/Validator/Context/ExecutionContext.php
@@ -20,6 +20,7 @@
use Symfony\Component\Validator\Mapping\MetadataInterface;
use Symfony\Component\Validator\Mapping\PropertyMetadataInterface;
use Symfony\Component\Validator\Util\PropertyPath;
+use Symfony\Component\Validator\Validator\LazyProperty;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\Validator\Violation\ConstraintViolationBuilder;
@@ -187,7 +188,7 @@ public function addViolation($message, array $parameters = [])
$parameters,
$this->root,
$this->propertyPath,
- $this->value,
+ $this->getValue(),
null,
null,
$this->constraint
@@ -206,7 +207,7 @@ public function buildViolation($message, array $parameters = [])
$parameters,
$this->root,
$this->propertyPath,
- $this->value,
+ $this->getValue(),
$this->translator,
$this->translationDomain
);
@@ -241,6 +242,10 @@ public function getRoot()
*/
public function getValue()
{
+ if ($this->value instanceof LazyProperty) {
+ return $this->value->getPropertyValue();
+ }
+
return $this->value;
}
diff --git a/src/Symfony/Component/Validator/Mapping/Factory/LazyLoadingMetadataFactory.php b/src/Symfony/Component/Validator/Mapping/Factory/LazyLoadingMetadataFactory.php
index 6bd16c1c7dcae..b17ff691fba48 100644
--- a/src/Symfony/Component/Validator/Mapping/Factory/LazyLoadingMetadataFactory.php
+++ b/src/Symfony/Component/Validator/Mapping/Factory/LazyLoadingMetadataFactory.php
@@ -117,34 +117,25 @@ public function getMetadataFor($value)
private function mergeConstraints(ClassMetadata $metadata)
{
+ if ($metadata->getReflectionClass()->isInterface()) {
+ return;
+ }
+
// Include constraints from the parent class
if ($parent = $metadata->getReflectionClass()->getParentClass()) {
$metadata->mergeConstraints($this->getMetadataFor($parent->name));
}
- $interfaces = $metadata->getReflectionClass()->getInterfaces();
-
- $interfaces = array_filter($interfaces, function ($interface) use ($parent, $interfaces) {
- $interfaceName = $interface->getName();
-
- if ($parent && $parent->implementsInterface($interfaceName)) {
- return false;
- }
-
- foreach ($interfaces as $i) {
- if ($i !== $interface && $i->implementsInterface($interfaceName)) {
- return false;
- }
- }
-
- return true;
- });
-
// Include constraints from all directly implemented interfaces
- foreach ($interfaces as $interface) {
+ foreach ($metadata->getReflectionClass()->getInterfaces() as $interface) {
if ('Symfony\Component\Validator\GroupSequenceProviderInterface' === $interface->name) {
continue;
}
+
+ if ($parent && \in_array($interface->getName(), $parent->getInterfaceNames(), true)) {
+ continue;
+ }
+
$metadata->mergeConstraints($this->getMetadataFor($interface->name));
}
}
diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.ru.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.ru.xlf
index 80911a9902910..3c03fd8525cca 100644
--- a/src/Symfony/Component/Validator/Resources/translations/validators.ru.xlf
+++ b/src/Symfony/Component/Validator/Resources/translations/validators.ru.xlf
@@ -370,6 +370,18 @@
This value is not a valid hostname.
Значение не является корректным именем хоста.
+
+ The number of elements in this collection should be a multiple of {{ compared_value }}.
+ Количество элементов в этой коллекции должно быть кратным {{ compared_value }}.
+
+
+ This value should satisfy at least one of the following constraints:
+ Значение должно удовлетворять как минимум одному из следующих ограничений:
+
+
+ Each element of this collection should satisfy its own set of constraints.
+ Каждый элемент этой коллекции должен удовлетворять своему собственному набору ограничений.
+
Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy
diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.uk.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.uk.xlf
index cba61915544a3..688e11fbe929c 100644
--- a/src/Symfony/Component/Validator/Resources/translations/validators.uk.xlf
+++ b/src/Symfony/Component/Validator/Resources/translations/validators.uk.xlf
@@ -366,6 +366,22 @@
This value should be between {{ min }} and {{ max }}.
Значення має бути між {{ min }} та {{ max }}.
+
+ This value is not a valid hostname.
+ Значення не є дійсним іменем хоста.
+
+
+ The number of elements in this collection should be a multiple of {{ compared_value }}.
+ Кількість елементів у цій колекції повинна бути кратною {{ compared_value }}.
+
+
+ This value should satisfy at least one of the following constraints:
+ Значення повинно задовольняти хоча б одному з наступних обмежень:
+
+
+ Each element of this collection should satisfy its own set of constraints.
+ Кожен елемент цієї колекції повинен задовольняти власному набору обмежень.
+
diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CollectionTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CollectionTest.php
index fef129cfa7494..254154dae7244 100644
--- a/src/Symfony/Component/Validator/Tests/Constraints/CollectionTest.php
+++ b/src/Symfony/Component/Validator/Tests/Constraints/CollectionTest.php
@@ -100,4 +100,16 @@ public function testAcceptRequiredConstraintAsOneElementArray()
$this->assertEquals($collection1, $collection2);
}
+
+ public function testConstraintHasDefaultGroupWithOptionalValues()
+ {
+ $constraint = new Collection([
+ 'foo' => new Required(),
+ 'bar' => new Optional(),
+ ]);
+
+ $this->assertEquals(['Default'], $constraint->groups);
+ $this->assertEquals(['Default'], $constraint->fields['foo']->groups);
+ $this->assertEquals(['Default'], $constraint->fields['bar']->groups);
+ }
}
diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CollectionValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CollectionValidatorTest.php
index e0ccdba754b0b..11913eb2307ac 100644
--- a/src/Symfony/Component/Validator/Tests/Constraints/CollectionValidatorTest.php
+++ b/src/Symfony/Component/Validator/Tests/Constraints/CollectionValidatorTest.php
@@ -143,6 +143,29 @@ public function testExtraFieldsDisallowed()
->assertRaised();
}
+ public function testExtraFieldsDisallowedWithOptionalValues()
+ {
+ $constraint = new Optional();
+
+ $data = $this->prepareTestData([
+ 'baz' => 6,
+ ]);
+
+ $this->validator->validate($data, new Collection([
+ 'fields' => [
+ 'foo' => $constraint,
+ ],
+ 'extraFieldsMessage' => 'myMessage',
+ ]));
+
+ $this->buildViolation('myMessage')
+ ->setParameter('{{ field }}', '"baz"')
+ ->atPath('property.path[baz]')
+ ->setInvalidValue(6)
+ ->setCode(Collection::NO_SUCH_FIELD_ERROR)
+ ->assertRaised();
+ }
+
// bug fix
public function testNullNotConsideredExtraField()
{
diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CompositeTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CompositeTest.php
index 2d42807821bb3..287fd6d6681cb 100644
--- a/src/Symfony/Component/Validator/Tests/Constraints/CompositeTest.php
+++ b/src/Symfony/Component/Validator/Tests/Constraints/CompositeTest.php
@@ -19,7 +19,7 @@
class ConcreteComposite extends Composite
{
- public $constraints;
+ public $constraints = [];
protected function getCompositeOption()
{
@@ -37,6 +37,30 @@ public function getDefaultOption()
*/
class CompositeTest extends TestCase
{
+ public function testConstraintHasDefaultGroup()
+ {
+ $constraint = new ConcreteComposite([
+ new NotNull(),
+ new NotBlank(),
+ ]);
+
+ $this->assertEquals(['Default'], $constraint->groups);
+ $this->assertEquals(['Default'], $constraint->constraints[0]->groups);
+ $this->assertEquals(['Default'], $constraint->constraints[1]->groups);
+ }
+
+ public function testNestedCompositeConstraintHasDefaultGroup()
+ {
+ $constraint = new ConcreteComposite([
+ new ConcreteComposite(),
+ new ConcreteComposite(),
+ ]);
+
+ $this->assertEquals(['Default'], $constraint->groups);
+ $this->assertEquals(['Default'], $constraint->constraints[0]->groups);
+ $this->assertEquals(['Default'], $constraint->constraints[1]->groups);
+ }
+
public function testMergeNestedGroupsIfNoExplicitParentGroup()
{
$constraint = new ConcreteComposite([
diff --git a/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php
index 0357172680c61..6bcc5c54c5928 100644
--- a/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php
+++ b/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php
@@ -122,6 +122,8 @@ public function getValidUrls()
['http://user.name:pass.word@symfony.com'],
['http://user-name@symfony.com'],
['http://user_name@symfony.com'],
+ ['http://u%24er:password@symfony.com'],
+ ['http://user:pa%24%24word@symfony.com'],
['http://symfony.com?'],
['http://symfony.com?query=1'],
['http://symfony.com/?query=1'],
@@ -168,6 +170,8 @@ public function getInvalidUrls()
['http://:password@@symfony.com'],
['http://username:passwordsymfony.com'],
['http://usern@me:password@symfony.com'],
+ ['http://nota%hex:password@symfony.com'],
+ ['http://username:nota%hex@symfony.com'],
['http://example.com/exploit.html?'],
['http://example.com/exploit.html?hel lo'],
['http://example.com/exploit.html?not_a%hex'],
diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/AbstractPropertyGetter.php b/src/Symfony/Component/Validator/Tests/Fixtures/AbstractPropertyGetter.php
new file mode 100644
index 0000000000000..3df0b9469ddf4
--- /dev/null
+++ b/src/Symfony/Component/Validator/Tests/Fixtures/AbstractPropertyGetter.php
@@ -0,0 +1,13 @@
+property;
+ }
+}
diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/ChildGetterInterface.php b/src/Symfony/Component/Validator/Tests/Fixtures/ChildGetterInterface.php
new file mode 100644
index 0000000000000..65c144bbeadb9
--- /dev/null
+++ b/src/Symfony/Component/Validator/Tests/Fixtures/ChildGetterInterface.php
@@ -0,0 +1,7 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Validator\Tests\Fixtures;
+
+class EntityWithGroupedConstraintOnMethods
+{
+ public $bar;
+
+ public function isValidInFoo()
+ {
+ return false;
+ }
+
+ public function getBar()
+ {
+ throw new \Exception('Should not be called');
+ }
+}
diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/PropertyGetter.php b/src/Symfony/Component/Validator/Tests/Fixtures/PropertyGetter.php
new file mode 100644
index 0000000000000..e1e14bb0075ee
--- /dev/null
+++ b/src/Symfony/Component/Validator/Tests/Fixtures/PropertyGetter.php
@@ -0,0 +1,12 @@
+ [
'Default',
'EntityParentInterface',
- 'EntityInterfaceB',
'Entity',
]]),
];
@@ -186,6 +188,15 @@ public function testGroupsFromParent()
$this->assertContains('EntityStaticCar', $groups);
$this->assertContains('EntityStaticVehicle', $groups);
}
+
+ public function testMultipathInterfaceConstraint()
+ {
+ $factory = new LazyLoadingMetadataFactory(new PropertyGetterInterfaceConstraintLoader());
+ $metadata = $factory->getMetadataFor(PropertyGetter::class);
+ $constraints = $metadata->getPropertyMetadata('property');
+
+ $this->assertCount(1, $constraints);
+ }
}
class TestLoader implements LoaderInterface
@@ -195,3 +206,15 @@ public function loadClassMetadata(ClassMetadata $metadata)
$metadata->addConstraint(new ConstraintA());
}
}
+
+class PropertyGetterInterfaceConstraintLoader implements LoaderInterface
+{
+ public function loadClassMetadata(ClassMetadata $metadata)
+ {
+ if (PropertyGetterInterface::class === $metadata->getClassName()) {
+ $metadata->addGetterConstraint('property', new NotBlank());
+ }
+
+ return true;
+ }
+}
diff --git a/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php b/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php
index 8109b6b9bfd4d..31871c3f9a1ed 100644
--- a/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php
+++ b/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php
@@ -14,14 +14,19 @@
use Symfony\Component\Translation\IdentityTranslator;
use Symfony\Component\Validator\Constraints\All;
use Symfony\Component\Validator\Constraints\Collection;
+use Symfony\Component\Validator\Constraints\GroupSequence;
+use Symfony\Component\Validator\Constraints\IsTrue;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
+use Symfony\Component\Validator\Constraints\NotNull;
use Symfony\Component\Validator\ConstraintValidatorFactory;
use Symfony\Component\Validator\Context\ExecutionContextFactory;
+use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
use Symfony\Component\Validator\Tests\Constraints\Fixtures\ChildA;
use Symfony\Component\Validator\Tests\Constraints\Fixtures\ChildB;
use Symfony\Component\Validator\Tests\Fixtures\Entity;
+use Symfony\Component\Validator\Tests\Fixtures\EntityWithGroupedConstraintOnMethods;
use Symfony\Component\Validator\Validator\RecursiveValidator;
class RecursiveValidatorTest extends AbstractTest
@@ -117,6 +122,25 @@ public function testCollectionConstraintValidateAllGroupsForNestedConstraints()
$this->assertInstanceOf(NotBlank::class, $violations->get(1)->getConstraint());
}
+ public function testGroupedMethodConstraintValidateInSequence()
+ {
+ $metadata = new ClassMetadata(EntityWithGroupedConstraintOnMethods::class);
+ $metadata->addPropertyConstraint('bar', new NotNull(['groups' => 'Foo']));
+ $metadata->addGetterMethodConstraint('validInFoo', 'isValidInFoo', new IsTrue(['groups' => 'Foo']));
+ $metadata->addGetterMethodConstraint('bar', 'getBar', new NotNull(['groups' => 'Bar']));
+
+ $this->metadataFactory->addMetadata($metadata);
+
+ $entity = new EntityWithGroupedConstraintOnMethods();
+ $groups = new GroupSequence(['EntityWithGroupedConstraintOnMethods', 'Foo', 'Bar']);
+
+ $violations = $this->validator->validate($entity, null, $groups);
+
+ $this->assertCount(2, $violations);
+ $this->assertInstanceOf(NotNull::class, $violations->get(0)->getConstraint());
+ $this->assertInstanceOf(IsTrue::class, $violations->get(1)->getConstraint());
+ }
+
public function testAllConstraintValidateAllGroupsForNestedConstraints()
{
$this->metadata->addPropertyConstraint('data', new All(['constraints' => [
diff --git a/src/Symfony/Component/Validator/Tests/Violation/ConstraintViolationBuilderTest.php b/src/Symfony/Component/Validator/Tests/Violation/ConstraintViolationBuilderTest.php
new file mode 100644
index 0000000000000..4121dc2622e96
--- /dev/null
+++ b/src/Symfony/Component/Validator/Tests/Violation/ConstraintViolationBuilderTest.php
@@ -0,0 +1,106 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Validator\Tests\Violation;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\IdentityTranslator;
+use Symfony\Component\Validator\Constraints\Valid;
+use Symfony\Component\Validator\ConstraintViolation;
+use Symfony\Component\Validator\ConstraintViolationList;
+use Symfony\Component\Validator\Test\ForwardCompatTestTrait;
+use Symfony\Component\Validator\Violation\ConstraintViolationBuilder;
+
+class ConstraintViolationBuilderTest extends TestCase
+{
+ use ForwardCompatTestTrait;
+
+ private $root;
+ private $violations;
+ private $messageTemplate = '%value% is invalid';
+ private $builder;
+
+ private function doSetUp()
+ {
+ $this->root = [
+ 'data' => [
+ 'foo' => 'bar',
+ 'baz' => 'foobar',
+ ],
+ ];
+ $this->violations = new ConstraintViolationList();
+ $this->builder = new ConstraintViolationBuilder($this->violations, new Valid(), $this->messageTemplate, [], $this->root, 'data', 'foo', new IdentityTranslator());
+ }
+
+ public function testAddViolation()
+ {
+ $this->builder->addViolation();
+
+ $this->assertViolationEquals(new ConstraintViolation($this->messageTemplate, $this->messageTemplate, [], $this->root, 'data', 'foo', null, null, new Valid()));
+ }
+
+ public function testAppendPropertyPath()
+ {
+ $this->builder
+ ->atPath('foo')
+ ->addViolation();
+
+ $this->assertViolationEquals(new ConstraintViolation($this->messageTemplate, $this->messageTemplate, [], $this->root, 'data.foo', 'foo', null, null, new Valid()));
+ }
+
+ public function testAppendMultiplePropertyPaths()
+ {
+ $this->builder
+ ->atPath('foo')
+ ->atPath('bar')
+ ->addViolation();
+
+ $this->assertViolationEquals(new ConstraintViolation($this->messageTemplate, $this->messageTemplate, [], $this->root, 'data.foo.bar', 'foo', null, null, new Valid()));
+ }
+
+ public function testCodeCanBeSet()
+ {
+ $this->builder
+ ->setCode(5)
+ ->addViolation();
+
+ $this->assertViolationEquals(new ConstraintViolation($this->messageTemplate, $this->messageTemplate, [], $this->root, 'data', 'foo', null, 5, new Valid()));
+ }
+
+ public function testCauseCanBeSet()
+ {
+ $cause = new \LogicException();
+
+ $this->builder
+ ->setCause($cause)
+ ->addViolation();
+
+ $this->assertViolationEquals(new ConstraintViolation($this->messageTemplate, $this->messageTemplate, [], $this->root, 'data', 'foo', null, null, new Valid(), $cause));
+ }
+
+ private function assertViolationEquals(ConstraintViolation $expectedViolation)
+ {
+ $this->assertCount(1, $this->violations);
+
+ $violation = $this->violations->get(0);
+
+ $this->assertSame($expectedViolation->getMessage(), $violation->getMessage());
+ $this->assertSame($expectedViolation->getMessageTemplate(), $violation->getMessageTemplate());
+ $this->assertSame($expectedViolation->getParameters(), $violation->getParameters());
+ $this->assertSame($expectedViolation->getPlural(), $violation->getPlural());
+ $this->assertSame($expectedViolation->getRoot(), $violation->getRoot());
+ $this->assertSame($expectedViolation->getPropertyPath(), $violation->getPropertyPath());
+ $this->assertSame($expectedViolation->getInvalidValue(), $violation->getInvalidValue());
+ $this->assertSame($expectedViolation->getCode(), $violation->getCode());
+ $this->assertEquals($expectedViolation->getConstraint(), $violation->getConstraint());
+ $this->assertSame($expectedViolation->getCause(), $violation->getCause());
+ }
+}
diff --git a/src/Symfony/Component/Validator/Validator/LazyProperty.php b/src/Symfony/Component/Validator/Validator/LazyProperty.php
new file mode 100644
index 0000000000000..a0799963c150f
--- /dev/null
+++ b/src/Symfony/Component/Validator/Validator/LazyProperty.php
@@ -0,0 +1,32 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Validator\Validator;
+
+/**
+ * A wrapper for a callable initializing a property from a getter.
+ *
+ * @internal
+ */
+class LazyProperty
+{
+ private $propertyValueCallback;
+
+ public function __construct(\Closure $propertyValueCallback)
+ {
+ $this->propertyValueCallback = $propertyValueCallback;
+ }
+
+ public function getPropertyValue()
+ {
+ return \call_user_func($this->propertyValueCallback);
+ }
+}
diff --git a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php
index 87a9974c171e0..38bd945a6ac53 100644
--- a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php
+++ b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php
@@ -27,6 +27,7 @@
use Symfony\Component\Validator\Mapping\ClassMetadataInterface;
use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
use Symfony\Component\Validator\Mapping\GenericMetadata;
+use Symfony\Component\Validator\Mapping\GetterMetadata;
use Symfony\Component\Validator\Mapping\MetadataInterface;
use Symfony\Component\Validator\Mapping\PropertyMetadataInterface;
use Symfony\Component\Validator\Mapping\TraversalStrategy;
@@ -535,7 +536,13 @@ private function validateClassNode($object, $cacheKey, ClassMetadataInterface $m
throw new UnsupportedMetadataException(sprintf('The property metadata instances should implement "Symfony\Component\Validator\Mapping\PropertyMetadataInterface", got: "%s".', \is_object($propertyMetadata) ? \get_class($propertyMetadata) : \gettype($propertyMetadata)));
}
- $propertyValue = $propertyMetadata->getPropertyValue($object);
+ if ($propertyMetadata instanceof GetterMetadata) {
+ $propertyValue = new LazyProperty(static function () use ($propertyMetadata, $object) {
+ return $propertyMetadata->getPropertyValue($object);
+ });
+ } else {
+ $propertyValue = $propertyMetadata->getPropertyValue($object);
+ }
$this->validateGenericNode(
$propertyValue,
@@ -800,6 +807,11 @@ private function validateInGroup($value, $cacheKey, MetadataInterface $metadata,
$validator = $this->validatorFactory->getInstance($constraint);
$validator->initialize($context);
+
+ if ($value instanceof LazyProperty) {
+ $value = $value->getPropertyValue();
+ }
+
$validator->validate($value, $constraint);
}
}
diff --git a/src/Symfony/Component/Workflow/MarkingStore/SingleStateMarkingStore.php b/src/Symfony/Component/Workflow/MarkingStore/SingleStateMarkingStore.php
index daccc65b41606..cd102f4ce67a2 100644
--- a/src/Symfony/Component/Workflow/MarkingStore/SingleStateMarkingStore.php
+++ b/src/Symfony/Component/Workflow/MarkingStore/SingleStateMarkingStore.php
@@ -44,7 +44,7 @@ public function getMarking($subject)
{
$placeName = $this->propertyAccessor->getValue($subject, $this->property);
- if (!$placeName) {
+ if (null === $placeName) {
return new Marking();
}
diff --git a/src/Symfony/Component/Workflow/Tests/MarkingStore/SingleStateMarkingStoreTest.php b/src/Symfony/Component/Workflow/Tests/MarkingStore/SingleStateMarkingStoreTest.php
index 2e00714d2762a..0db37986cfc70 100644
--- a/src/Symfony/Component/Workflow/Tests/MarkingStore/SingleStateMarkingStoreTest.php
+++ b/src/Symfony/Component/Workflow/Tests/MarkingStore/SingleStateMarkingStoreTest.php
@@ -30,4 +30,17 @@ public function testGetSetMarking()
$this->assertEquals($marking, $marking2);
}
+
+ public function testAlmostEmptyPlaceName()
+ {
+ $subject = new \stdClass();
+ $subject->myMarks = 0;
+
+ $markingStore = new SingleStateMarkingStore('myMarks');
+
+ $marking = $markingStore->getMarking($subject);
+
+ $this->assertInstanceOf(Marking::class, $marking);
+ $this->assertCount(1, $marking->getPlaces());
+ }
}
diff --git a/src/Symfony/Component/Yaml/Escaper.php b/src/Symfony/Component/Yaml/Escaper.php
index 4d3ce244c2ada..9413d7a2c4804 100644
--- a/src/Symfony/Component/Yaml/Escaper.php
+++ b/src/Symfony/Component/Yaml/Escaper.php
@@ -22,7 +22,7 @@
class Escaper
{
// Characters that would cause a dumped string to require double quoting.
- const REGEX_CHARACTER_TO_ESCAPE = "[\\x00-\\x1f]|\xc2\x85|\xc2\xa0|\xe2\x80\xa8|\xe2\x80\xa9";
+ const REGEX_CHARACTER_TO_ESCAPE = "[\\x00-\\x1f]|\x7f|\xc2\x85|\xc2\xa0|\xe2\x80\xa8|\xe2\x80\xa9";
// Mapping arrays for escaping a double quoted string. The backslash is
// first to ensure proper escaping because str_replace operates iteratively
@@ -33,6 +33,7 @@ class Escaper
"\x08", "\x09", "\x0a", "\x0b", "\x0c", "\x0d", "\x0e", "\x0f",
"\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17",
"\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f",
+ "\x7f",
"\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9",
];
private static $escaped = ['\\\\', '\\"', '\\\\', '\\"',
@@ -40,6 +41,7 @@ class Escaper
'\\b', '\\t', '\\n', '\\v', '\\f', '\\r', '\\x0e', '\\x0f',
'\\x10', '\\x11', '\\x12', '\\x13', '\\x14', '\\x15', '\\x16', '\\x17',
'\\x18', '\\x19', '\\x1a', '\\e', '\\x1c', '\\x1d', '\\x1e', '\\x1f',
+ '\\x7f',
'\\N', '\\_', '\\L', '\\P',
];
diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php
index 7198d077980c2..4c7d37428e474 100644
--- a/src/Symfony/Component/Yaml/Inline.php
+++ b/src/Symfony/Component/Yaml/Inline.php
@@ -720,7 +720,7 @@ private static function evaluateScalar($scalar, $flags, $references = [])
throw new ParseException(sprintf('The constant "%s" is not defined.', $const), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}
if (self::$exceptionOnInvalidType) {
- throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Have you forgotten to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
+ throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Did you forget to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}
return null;
@@ -738,7 +738,7 @@ private static function evaluateScalar($scalar, $flags, $references = [])
throw new ParseException(sprintf('The constant "%s" is not defined.', $const), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}
if (self::$exceptionOnInvalidType) {
- throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Have you forgotten to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
+ throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Did you forget to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}
return null;
diff --git a/src/Symfony/Component/Yaml/Tests/DumperTest.php b/src/Symfony/Component/Yaml/Tests/DumperTest.php
index bb2a575eebdab..e4dc49f331d72 100644
--- a/src/Symfony/Component/Yaml/Tests/DumperTest.php
+++ b/src/Symfony/Component/Yaml/Tests/DumperTest.php
@@ -289,6 +289,7 @@ public function getEscapeSequences()
'double-quote' => ['"', "'\"'"],
'slash' => ['/', '/'],
'backslash' => ['\\', '\\'],
+ 'del' => ["\x7f", '"\x7f"'],
'next-line' => ["\xC2\x85", '"\\N"'],
'non-breaking-space' => ["\xc2\xa0", '"\\_"'],
'line-separator' => ["\xE2\x80\xA8", '"\\L"'],