diff --git a/.travis.yml b/.travis.yml
index a5246b3eafb58..99e6310eb2b7e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -269,7 +269,12 @@ install:
export PHP=$1
if [[ !$deps && $PHP = 7.2 ]]; then
- tfold src/Symfony/Component/HttpClient.h2push "$COMPOSER_UP symfony/contracts && docker run -it --rm -v $(pwd):/app -v $(phpenv which composer):/usr/local/bin/composer -v /usr/local/bin/vulcain:/usr/local/bin/vulcain -w /app php:7.3-alpine ./phpunit src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php --filter testHttp2Push"
+ phpenv global $PHP
+ tfold 'composer update' $COMPOSER_UP
+ [ -d .phpunit ] && mv .phpunit .phpunit.bak
+ tfold src/Symfony/Component/HttpClient.h2push "docker run -it --rm -v $(pwd):/app -v $(phpenv which composer):/usr/local/bin/composer -v /usr/local/bin/vulcain:/usr/local/bin/vulcain -w /app php:7.3-alpine ./phpunit src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php --filter testHttp2Push"
+ sudo rm .phpunit -rf
+ [ -d .phpunit.bak ] && mv .phpunit.bak .phpunit
fi
if [[ $PHP != 7.4* && $PHP != $TRAVIS_PHP_VERSION && $TRAVIS_PULL_REQUEST != false ]]; then
@@ -278,7 +283,9 @@ install:
fi
phpenv global $PHP
([[ $deps ]] && cd src/Symfony/Component/HttpFoundation; cp composer.json composer.bak; composer config platform.ext-mongodb 1.6.0; composer require --dev --no-update mongodb/mongodb ~1.5.0)
- tfold 'composer update' $COMPOSER_UP
+ if [[ $deps || $PHP != 7.2 ]]; then
+ tfold 'composer update' $COMPOSER_UP
+ fi
tfold 'phpunit install' ./phpunit install
if [[ $deps = high ]]; then
echo "$COMPONENTS" | parallel --gnu "tfold {} 'cd {} && $COMPOSER_UP && $PHPUNIT_X$LEGACY'" || X=1
diff --git a/CHANGELOG-4.4.md b/CHANGELOG-4.4.md
index 68eceda5b9742..be58f04b95501 100644
--- a/CHANGELOG-4.4.md
+++ b/CHANGELOG-4.4.md
@@ -7,6 +7,56 @@ in 4.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/v4.4.0...v4.4.1
+* 4.4.8 (2020-04-28)
+
+ * bug #36536 [Cache] Allow invalidateTags calls to be traced by data collector (l-vo)
+ * 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 #36519 [FrameworkBundle] debug:autowiring: Fix wrong display when using class_alias (weaverryan)
+ * bug #36454 [DependencyInjection][ServiceSubscriber] Support late aliases (fancyweb)
+ * bug #36498 [Security/Core] fix escape for username in LdapBindAuthenticationProvider.php (stoccc)
+ * bug #36506 [FrameworkBundle] Fix session.attribute_bag service definition (fancyweb)
+ * bug #36500 [Routing][PrefixTrait] Add the _locale requirement (fancyweb)
+ * bug #36457 [Cache] CacheItem with tag is never a hit after expired (alexander-schranz, nicolas-grekas)
+ * 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 #36467 Mailer from sender fixes (fabpot)
+ * bug #36408 [PhpUnitBridge] add PolyfillTestCaseTrait::expectExceptionMessageMatches to provide FC with recent phpunit versions (soyuka)
+ * bug #36447 Remove return type for Twig function workflow_metadata() (gisostallenberg)
+ * bug #36449 [Messenger] Make sure redis transports are initialized correctly (Seldaek)
+ * 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 #36422 [HttpClient] fix HTTP/2 support on non-SSL connections - CurlHttpClient only (nicolas-grekas)
+ * bug #36417 Force ping after transport exception (oesteve)
+ * bug #35591 [Validator] do not merge constraints within interfaces (greedyivan)
+ * bug #36377 [HttpClient] Fix scoped client without query option configuration (X-Coder264)
+ * bug #36387 [DI] fix detecting short service syntax in yaml (nicolas-grekas)
+ * bug #36392 [DI] add missing property declarations in InlineServiceConfigurator (nicolas-grekas)
+ * bug #36400 Allowing empty secrets to be set (weaverryan)
+ * bug #36380 [Process] Fixed input/output error on PHP 7.4 (mbardelmeijer)
+ * bug #36376 [Workflow] Use a strict comparison when retrieving raw marking in MarkingStore (lyrixx)
+ * 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 #36342 [HttpKernel][FrameworkBundle] fix compat with Debug component (nicolas-grekas)
+ * 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 #36335 [Security] Track session usage whenever a new token is set (wouterj)
+ * bug #36332 [Serializer] Fix unitialized properties (from PHP 7.4.2) when serializing context for the cache key (alanpoulain)
+ * bug #36337 [MonologBridge] Fix $level type (fancyweb)
+ * bug #36223 [Security][Http][SwitchUserListener] Ignore all non existent username protection errors (fancyweb)
+ * bug #36239 [HttpKernel][LoggerDataCollector] Prevent keys collisions in the sanitized logs processing (fancyweb)
+ * bug #36245 [Validator] Fixed calling getters before resolving groups (HeahDude)
+ * bug #36265 Fix the reporting of deprecations in twig:lint (stof)
+ * bug #36283 [Security] forward multiple attributes voting flag (xabbuh)
+
* 4.4.7 (2020-03-30)
* security #cve-2020-5255 [HttpFoundation] Do not set the default Content-Type based on the Accept header (yceruto)
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/composer.json b/composer.json
index 2b089d9d48fba..ae4553377613e 100644
--- a/composer.json
+++ b/composer.json
@@ -118,7 +118,7 @@
"psr/http-client": "^1.0",
"psr/simple-cache": "^1.0",
"egulias/email-validator": "~1.2,>=1.2.8|~2.0",
- "symfony/phpunit-bridge": "^3.4.31|^4.3.4|~5.0",
+ "symfony/phpunit-bridge": "^5.0.8",
"symfony/security-acl": "~2.8|~3.0",
"phpdocumentor/reflection-docblock": "^3.0|^4.0",
"twig/cssinliner-extra": "^2.12",
diff --git a/phpunit b/phpunit
index c89d2e400b602..fbce26d8edcca 100755
--- a/phpunit
+++ b/phpunit
@@ -1,7 +1,7 @@
#!/usr/bin/env php
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/Monolog/Command/ServerLogCommand.php b/src/Symfony/Bridge/Monolog/Command/ServerLogCommand.php
index 60e7341db9e2c..eb1a4dc257677 100644
--- a/src/Symfony/Bridge/Monolog/Command/ServerLogCommand.php
+++ b/src/Symfony/Bridge/Monolog/Command/ServerLogCommand.php
@@ -102,7 +102,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/Bridge/Monolog/Handler/ElasticsearchLogstashHandler.php b/src/Symfony/Bridge/Monolog/Handler/ElasticsearchLogstashHandler.php
index b8b0f7b9e99cb..99ba3bbc3d94a 100644
--- a/src/Symfony/Bridge/Monolog/Handler/ElasticsearchLogstashHandler.php
+++ b/src/Symfony/Bridge/Monolog/Handler/ElasticsearchLogstashHandler.php
@@ -49,7 +49,10 @@ class ElasticsearchLogstashHandler extends AbstractHandler
private $client;
private $responses;
- public function __construct(string $endpoint = 'http://127.0.0.1:9200', string $index = 'monolog', HttpClientInterface $client = null, int $level = Logger::DEBUG, bool $bubble = true)
+ /**
+ * @param string|int $level The minimum logging level at which this handler will be triggered
+ */
+ public function __construct(string $endpoint = 'http://127.0.0.1:9200', string $index = 'monolog', HttpClientInterface $client = null, $level = Logger::DEBUG, bool $bubble = true)
{
if (!interface_exists(HttpClientInterface::class)) {
throw new \LogicException(sprintf('The "%s" handler needs an HTTP client. Try running "composer require symfony/http-client".', __CLASS__));
diff --git a/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php b/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php
index 28582d31725a3..e5a26ccd221e2 100644
--- a/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php
+++ b/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php
@@ -25,7 +25,10 @@ class ServerLogHandler extends AbstractHandler
private $context;
private $socket;
- public function __construct(string $host, int $level = Logger::DEBUG, bool $bubble = true, array $context = [])
+ /**
+ * @param string|int $level The minimum logging level at which this handler will be triggered
+ */
+ public function __construct(string $host, $level = Logger::DEBUG, bool $bubble = true, array $context = [])
{
parent::__construct($level, $bubble);
diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/PolyfillTestCaseTrait.php b/src/Symfony/Bridge/PhpUnit/Legacy/PolyfillTestCaseTrait.php
index ebb9db2c94acb..cb3fbf44903bd 100644
--- a/src/Symfony/Bridge/PhpUnit/Legacy/PolyfillTestCaseTrait.php
+++ b/src/Symfony/Bridge/PhpUnit/Legacy/PolyfillTestCaseTrait.php
@@ -95,6 +95,16 @@ public function expectExceptionMessage($message)
$property->setValue($this, $message);
}
+ /**
+ * @param string $messageRegExp
+ *
+ * @return void
+ */
+ public function expectExceptionMessageMatches($messageRegExp)
+ {
+ $this->expectExceptionMessageRegExp($messageRegExp);
+ }
+
/**
* @param string $messageRegExp
*
diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php
index 1e030825e6fde..f9a1cadb49652 100644
--- a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php
+++ b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php
@@ -48,7 +48,12 @@ class SymfonyTestsListenerTrait
*/
public function __construct(array $mockedNamespaces = [])
{
- Blacklist::$blacklistedClassNames['\Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait'] = 2;
+ if (method_exists(Blacklist::class, 'addDirectory')) {
+ (new BlackList())->getBlacklistedDirectories();
+ Blacklist::addDirectory(\dirname((new \ReflectionClass(__CLASS__))->getFileName(), 2));
+ } else {
+ Blacklist::$blacklistedClassNames[__CLASS__] = 2;
+ }
$enableDebugClassLoader = class_exists(DebugClassLoader::class) || class_exists(LegacyDebugClassLoader::class);
diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php
index 401aafde162a4..dc2730bc9a620 100644
--- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php
+++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php
@@ -215,9 +215,10 @@
if (!class_exists('SymfonyBlacklistPhpunit', false)) {
class SymfonyBlacklistPhpunit {}
}
-if (class_exists('PHPUnit_Util_Blacklist')) {
- PHPUnit_Util_Blacklist::$blacklistedClassNames['SymfonyBlacklistPhpunit'] = 1;
- PHPUnit_Util_Blacklist::$blacklistedClassNames['SymfonyBlacklistSimplePhpunit'] = 1;
+if (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 da2e101784961..9bcabc69dc44f 100644
--- a/src/Symfony/Bridge/PhpUnit/composer.json
+++ b/src/Symfony/Bridge/PhpUnit/composer.json
@@ -24,7 +24,7 @@
"symfony/error-handler": "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/Bridge/Twig/Command/LintCommand.php b/src/Symfony/Bridge/Twig/Command/LintCommand.php
index fa12718818815..16f960e9f74c0 100644
--- a/src/Symfony/Bridge/Twig/Command/LintCommand.php
+++ b/src/Symfony/Bridge/Twig/Command/LintCommand.php
@@ -110,7 +110,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
$prevErrorHandler = set_error_handler(static function ($level, $message, $file, $line) use (&$prevErrorHandler) {
if (E_USER_DEPRECATED === $level) {
$templateLine = 0;
- if (preg_match('/ at line (\d+) /', $message, $matches)) {
+ if (preg_match('/ at line (\d+)[ .]/', $message, $matches)) {
$templateLine = $matches[1];
}
@@ -236,6 +236,14 @@ private function renderException(OutputInterface $output, string $template, Erro
$output->text(sprintf(' ERROR (line %s)', $line));
}
+ // If the line is not known (this might happen for deprecations if we fail at detecting the line for instance),
+ // we render the message without context, to ensure the message is displayed.
+ if ($line <= 0) {
+ $output->text(sprintf(' >> %s ', $exception->getRawMessage()));
+
+ return;
+ }
+
foreach ($this->getContext($template, $line) as $lineNumber => $code) {
$output->text(sprintf(
'%s %-6s %s',
diff --git a/src/Symfony/Bridge/Twig/Extension/WorkflowExtension.php b/src/Symfony/Bridge/Twig/Extension/WorkflowExtension.php
index d5a2e759aa34a..b5f3badea88fd 100644
--- a/src/Symfony/Bridge/Twig/Extension/WorkflowExtension.php
+++ b/src/Symfony/Bridge/Twig/Extension/WorkflowExtension.php
@@ -117,7 +117,7 @@ public function getMarkedPlaces($subject, $placesNameOnly = true, $name = null)
* Use a string (the place name) to get place metadata
* Use a Transition instance to get transition metadata
*/
- public function getMetadata($subject, string $key, $metadataSubject = null, string $name = null): ?string
+ public function getMetadata($subject, string $key, $metadataSubject = null, string $name = null)
{
return $this
->workflowRegistry
diff --git a/src/Symfony/Bridge/Twig/Mime/NotificationEmail.php b/src/Symfony/Bridge/Twig/Mime/NotificationEmail.php
index 6d9bffcfcc47e..e6b28c4db43d2 100644
--- a/src/Symfony/Bridge/Twig/Mime/NotificationEmail.php
+++ b/src/Symfony/Bridge/Twig/Mime/NotificationEmail.php
@@ -51,7 +51,7 @@ public function __construct(Headers $headers = null, AbstractPart $body = null)
}
if ($missingPackages) {
- throw new \LogicException(sprintf('You cannot use "%s" if the "%s" Twig extension%s not available; try running "composer require "%s"".', static::class, implode('" and "', $missingPackages), \count($missingPackages) > 1 ? 's are' : ' is', implode(' ', array_keys($missingPackages))));
+ throw new \LogicException(sprintf('You cannot use "%s" if the "%s" Twig extension%s not available; try running "%s".', static::class, implode('" and "', $missingPackages), \count($missingPackages) > 1 ? 's are' : ' is', 'composer require '.implode(' ', array_keys($missingPackages))));
}
parent::__construct($headers, $body);
diff --git a/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php b/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php
index 46c0883722a20..8970bb497764e 100644
--- a/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php
+++ b/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php
@@ -18,6 +18,7 @@
use Symfony\Component\Console\Tester\CommandTester;
use Twig\Environment;
use Twig\Loader\FilesystemLoader;
+use Twig\TwigFilter;
class LintCommandTest extends TestCase
{
@@ -66,6 +67,34 @@ public function testLintFileCompileTimeException()
$this->assertRegExp('/ERROR in \S+ \(line /', trim($tester->getDisplay()));
}
+ /**
+ * When deprecations are not reported by the command, the testsuite reporter will catch them so we need to mark the test as legacy.
+ *
+ * @group legacy
+ */
+ public function testLintFileWithNotReportedDeprecation()
+ {
+ $tester = $this->createCommandTester();
+ $filename = $this->createFile('{{ foo|deprecated_filter }}');
+
+ $ret = $tester->execute(['filename' => [$filename]], ['verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false]);
+
+ $this->assertEquals(0, $ret, 'Returns 0 in case of success');
+ $this->assertStringContainsString('OK in', trim($tester->getDisplay()));
+ }
+
+ public function testLintFileWithReportedDeprecation()
+ {
+ $tester = $this->createCommandTester();
+ $filename = $this->createFile('{{ foo|deprecated_filter }}');
+
+ $ret = $tester->execute(['filename' => [$filename], '--show-deprecations' => true], ['verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false]);
+
+ $this->assertEquals(1, $ret, 'Returns 1 in case of error');
+ $this->assertRegExp('/ERROR in \S+ \(line 1\)/', trim($tester->getDisplay()));
+ $this->assertStringContainsString('Filter "deprecated_filter" is deprecated', trim($tester->getDisplay()));
+ }
+
/**
* @group tty
*/
@@ -80,7 +109,12 @@ public function testLintDefaultPaths()
private function createCommandTester(): CommandTester
{
- $command = new LintCommand(new Environment(new FilesystemLoader(\dirname(__DIR__).'/Fixtures/templates/')));
+ $environment = new Environment(new FilesystemLoader(\dirname(__DIR__).'/Fixtures/templates/'));
+ $environment->addFilter(new TwigFilter('deprecated_filter', function ($v) {
+ return $v;
+ }, ['deprecated' => true]));
+
+ $command = new LintCommand($environment);
$application = new Application();
$application->add($command);
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/DebugAutowiringCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/DebugAutowiringCommand.php
index 04d391da17775..32bd630f32516 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/DebugAutowiringCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/DebugAutowiringCommand.php
@@ -103,9 +103,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$serviceIdsNb = 0;
foreach ($serviceIds as $serviceId) {
$text = [];
+ $resolvedServiceId = $serviceId;
if (0 !== strpos($serviceId, $previousId)) {
$text[] = '';
- if ('' !== $description = Descriptor::getClassDescription($serviceId, $serviceId)) {
+ if ('' !== $description = Descriptor::getClassDescription($serviceId, $resolvedServiceId)) {
if (isset($hasAlias[$serviceId])) {
continue;
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsSetCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsSetCommand.php
index 5cca8d7011fac..91e0031299c3b 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsSetCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsSetCommand.php
@@ -96,6 +96,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$value = strtr(substr(base64_encode(random_bytes($random)), 0, $random), '+/', '-_');
} elseif (!$file = $input->getArgument('file')) {
$value = $io->askHidden('Please type the secret value');
+
+ if (null === $value) {
+ $io->warning('No value provided: using empty string');
+ $value = '';
+ }
} elseif ('-' === $file) {
$value = file_get_contents('php://stdin');
} elseif (is_file($file) && is_readable($file)) {
@@ -106,12 +111,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
throw new \InvalidArgumentException(sprintf('File is not readable: "%s".', $file));
}
- if (null === $value) {
- $io->warning('No value provided, aborting.');
-
- return 1;
- }
-
if ($vault->generateKeys()) {
$io->success($vault->getLastMessage());
diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php
index 45c995a467371..ee5e97c203e6a 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php
@@ -355,7 +355,7 @@ protected function describeContainerDefinition(Definition $definition, array $op
protected function describeContainerAlias(Alias $alias, array $options = [], ContainerBuilder $builder = null)
{
- if ($alias->isPublic()) {
+ if ($alias->isPublic() && !$alias->isPrivate()) {
$options['output']->comment(sprintf('This service is a public alias for the service %s ', (string) $alias));
} else {
$options['output']->comment(sprintf('This service is a private alias for the service %s ', (string) $alias));
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
index 105695c963d91..13c63ff9988d5 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
@@ -1482,7 +1482,7 @@ private function addHttpClientSection(ArrayNodeDefinition $rootNode)
->thenInvalid('Either "scope" or "base_uri" should be defined.')
->end()
->validate()
- ->ifTrue(function ($v) { return isset($v['query']) && !isset($v['base_uri']); })
+ ->ifTrue(function ($v) { return !empty($v['query']) && !isset($v['base_uri']); })
->thenInvalid('"query" applies to "base_uri" but no base URI is defined.')
->end()
->children()
diff --git a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
index b505daf131c36..0dc2860229bdf 100644
--- a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
+++ b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
@@ -35,6 +35,7 @@
use Symfony\Component\Cache\DependencyInjection\CachePoolPrunerPass;
use Symfony\Component\Config\Resource\ClassExistenceResource;
use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass;
+use Symfony\Component\Debug\ErrorHandler as LegacyErrorHandler;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\Compiler\RegisterReverseContainerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -89,6 +90,9 @@ class FrameworkBundle extends Bundle
public function boot()
{
ErrorHandler::register(null, false)->throwAt($this->container->getParameter('debug.error_handler.throw_at'), true);
+ if (class_exists(LegacyErrorHandler::class, false)) {
+ LegacyErrorHandler::register(null, false)->throwAt($this->container->getParameter('debug.error_handler.throw_at'), true);
+ }
if ($this->container->getParameter('kernel.http_method_override')) {
Request::enableHttpMethodParameterOverride();
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml
index 26f08f78895ed..c9b17f311f576 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/Fixtures/CustomPathBundle/src/CustomPathBundle.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/src/CustomPathBundle.php
index ad7194de97b0e..f1f2bdc746e9e 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/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/http_client_scoped_without_query_option.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/http_client_scoped_without_query_option.php
new file mode 100644
index 0000000000000..0d3dc88472f84
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/http_client_scoped_without_query_option.php
@@ -0,0 +1,11 @@
+loadFromExtension('framework', [
+ 'http_client' => [
+ 'scoped_clients' => [
+ 'foo' => [
+ 'scope' => '.*',
+ ],
+ ],
+ ],
+]);
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/http_client_scoped_without_query_option.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/http_client_scoped_without_query_option.xml
new file mode 100644
index 0000000000000..43043aeda2a01
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/http_client_scoped_without_query_option.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/http_client_scoped_without_query_option.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/http_client_scoped_without_query_option.yml
new file mode 100644
index 0000000000000..ecfc9d41fd4c3
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/http_client_scoped_without_query_option.yml
@@ -0,0 +1,5 @@
+framework:
+ http_client:
+ scoped_clients:
+ foo:
+ scope: '.*'
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
index edcdb7aae487f..9a8fc0776f0af 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
@@ -1547,6 +1547,14 @@ public function testHttpClientDefaultOptions()
$this->assertSame(ScopingHttpClient::class, $container->getDefinition('foo')->getClass());
}
+ public function testScopedHttpClientWithoutQueryOption()
+ {
+ $container = $this->createContainerFromFile('http_client_scoped_without_query_option');
+
+ $this->assertTrue($container->hasDefinition('foo'), 'should have the "foo" service.');
+ $this->assertSame(ScopingHttpClient::class, $container->getDefinition('foo')->getClass());
+ }
+
public function testHttpClientOverrideDefaultOptions()
{
$container = $this->createContainerFromFile('http_client_override_default_options');
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/ClassAliasExampleClass.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/ClassAliasExampleClass.php
new file mode 100644
index 0000000000000..c1298658597b1
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/ClassAliasExampleClass.php
@@ -0,0 +1,14 @@
+run(['command' => 'debug:autowiring', 'search' => 'redirect', '--all' => true]);
$this->assertStringContainsString('Pro-tip: use interfaces in your type-hints instead of classes to benefit from the dependency inversion principle.', $tester->getDisplay());
}
+
+ public function testNotConfusedByClassAliases()
+ {
+ static::bootKernel(['test_case' => 'ContainerDebug', 'root_config' => 'config.yml']);
+
+ $application = new Application(static::$kernel);
+ $application->setAutoExit(false);
+
+ $tester = new ApplicationTester($application);
+ $tester->run(['command' => 'debug:autowiring', 'search' => 'ClassAlias']);
+ $this->assertStringContainsString('Symfony\Bundle\FrameworkBundle\Tests\Fixtures\ClassAliasExampleClass (public)', $tester->getDisplay());
+ }
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/config.yml
index 0cc73dbc81422..25c1c784298b5 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/config.yml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/config.yml
@@ -13,6 +13,7 @@ services:
public: false
Symfony\Bundle\FrameworkBundle\Tests\Fixtures\BackslashClass:
class: Symfony\Bundle\FrameworkBundle\Tests\Fixtures\BackslashClass
+ Symfony\Bundle\FrameworkBundle\Tests\Fixtures\ClassAliasExampleClass: '@public'
env:
class: stdClass
arguments:
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
index b9258a93f8d1b..42114072d27fc 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
@@ -558,7 +558,7 @@ private function createEncoder(array $config)
$config['algorithm'] = 'native';
$config['native_algorithm'] = PASSWORD_ARGON2I;
} else {
- throw new InvalidConfigurationException(sprintf('Algorithm "argon2i" is not available. Either use %s"auto" or upgrade to PHP 7.2+ instead.', \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13') ? '"argon2id", ' : ''));
+ throw new InvalidConfigurationException(sprintf('Algorithm "argon2i" is not available. Either use "%s" or upgrade to PHP 7.2+ instead.', \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13') ? 'argon2id", "auto' : 'auto'));
}
return $this->createEncoder($config);
@@ -571,7 +571,7 @@ private function createEncoder(array $config)
$config['algorithm'] = 'native';
$config['native_algorithm'] = PASSWORD_ARGON2ID;
} else {
- throw new InvalidConfigurationException(sprintf('Algorithm "argon2id" is not available. Either use %s"auto", upgrade to PHP 7.3+ or use libsodium 1.0.15+ instead.', \defined('PASSWORD_ARGON2I') || $hasSodium ? '"argon2i", ' : ''));
+ throw new InvalidConfigurationException(sprintf('Algorithm "argon2id" is not available. Either use "%s", upgrade to PHP 7.3+ or use libsodium 1.0.15+ instead.', \defined('PASSWORD_ARGON2I') || $hasSodium ? 'argon2i", "auto' : 'auto'));
}
return $this->createEncoder($config);
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/GuardedBundle/AppCustomAuthenticator.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/GuardedBundle/AppCustomAuthenticator.php
index fef2732759fa1..22d378835e4c0 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/GuardedBundle/AppCustomAuthenticator.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/GuardedBundle/AppCustomAuthenticator.php
@@ -23,7 +23,7 @@ class AppCustomAuthenticator extends AbstractGuardAuthenticator
{
public function supports(Request $request)
{
- return true;
+ return '/manual_login' !== $request->getPathInfo() && '/profile' !== $request->getPathInfo();
}
public function getCredentials(Request $request)
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/GuardedBundle/AuthenticationController.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/GuardedBundle/AuthenticationController.php
new file mode 100644
index 0000000000000..9833d05513833
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/GuardedBundle/AuthenticationController.php
@@ -0,0 +1,38 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\GuardedBundle;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Security\Core\User\User;
+use Symfony\Component\Security\Core\User\UserInterface;
+use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
+use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;
+
+class AuthenticationController
+{
+ public function manualLoginAction(GuardAuthenticatorHandler $guardAuthenticatorHandler, Request $request)
+ {
+ $guardAuthenticatorHandler->authenticateWithToken(new PostAuthenticationGuardToken(new User('Jane', 'test', ['ROLE_USER']), 'secure', ['ROLE_USER']), $request, 'secure');
+
+ return new Response('Logged in.');
+ }
+
+ public function profileAction(UserInterface $user = null)
+ {
+ if (null === $user) {
+ return new Response('Not logged in.');
+ }
+
+ return new Response('Username: '.$user->getUsername());
+ }
+}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/GuardedTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/GuardedTest.php
index bb0969c36a2fd..83cd4118d76e4 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/GuardedTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/GuardedTest.php
@@ -21,4 +21,14 @@ public function testGuarded()
$this->assertSame(418, $client->getResponse()->getStatusCode());
}
+
+ public function testManualLogin()
+ {
+ $client = $this->createClient(['debug' => true, 'test_case' => 'Guarded', 'root_config' => 'config.yml']);
+
+ $client->request('GET', '/manual_login');
+ $client->request('GET', '/profile');
+
+ $this->assertSame('Username: Jane', $client->getResponse()->getContent());
+ }
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Guarded/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Guarded/config.yml
index 2d1f779a530ec..7f87c307d28b5 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Guarded/config.yml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Guarded/config.yml
@@ -10,8 +10,19 @@ framework:
services:
logger: { class: Psr\Log\NullLogger }
Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\GuardedBundle\AppCustomAuthenticator: ~
+ Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\GuardedBundle\AuthenticationController:
+ tags: [controller.service_arguments]
security:
+ encoders:
+ Symfony\Component\Security\Core\User\User: plaintext
+
+ providers:
+ in_memory:
+ memory:
+ users:
+ Jane: { password: test, roles: [ROLE_USER] }
+
firewalls:
secure:
pattern: ^/
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Guarded/routing.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Guarded/routing.yml
index 4d11154375219..146aa811a143d 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Guarded/routing.yml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Guarded/routing.yml
@@ -3,3 +3,12 @@ main:
defaults:
_controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction
path: /app
+profile:
+ path: /profile
+ defaults:
+ _controller: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\GuardedBundle\AuthenticationController::profileAction
+
+manual_login:
+ path: /manual_login
+ defaults:
+ _controller: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\GuardedBundle\AuthenticationController::manualLoginAction
diff --git a/src/Symfony/Bundle/TwigBundle/Tests/Loader/FilesystemLoaderTest.php b/src/Symfony/Bundle/TwigBundle/Tests/Loader/FilesystemLoaderTest.php
index 2d678fa8f3502..b760cca92627e 100644
--- a/src/Symfony/Bundle/TwigBundle/Tests/Loader/FilesystemLoaderTest.php
+++ b/src/Symfony/Bundle/TwigBundle/Tests/Loader/FilesystemLoaderTest.php
@@ -101,7 +101,7 @@ public function testTwigErrorIfLocatorReturnsFalse()
public function testTwigErrorIfTemplateDoesNotExist()
{
$this->expectException('Twig\Error\LoaderError');
- $this->expectExceptionMessageRegExp('/Unable to find template "name\.format\.engine" \(looked into: .*Tests.Loader.\.\..DependencyInjection.Fixtures.templates\)/');
+ $this->expectExceptionMessageMatches('/Unable to find template "name\.format\.engine" \(looked into: .*Tests.Loader.\.\..DependencyInjection.Fixtures.templates\)/');
$parser = $this->getMockBuilder('Symfony\Component\Templating\TemplateNameParserInterface')->getMock();
$locator = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')->getMock();
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Csp/ContentSecurityPolicyHandler.php b/src/Symfony/Bundle/WebProfilerBundle/Csp/ContentSecurityPolicyHandler.php
index 0679d2bf6d04a..7c111a8ba3560 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Csp/ContentSecurityPolicyHandler.php
+++ b/src/Symfony/Bundle/WebProfilerBundle/Csp/ContentSecurityPolicyHandler.php
@@ -124,7 +124,7 @@ private function updateCspHeaders(Response $response, array $nonces = []): array
$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 62187dbab4f31..9284a0a708d36 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig
+++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig
@@ -52,7 +52,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 %}
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 1eb1916a3f129..57feb65cc1f80 100644
--- a/src/Symfony/Bundle/WebServerBundle/Command/ServerLogCommand.php
+++ b/src/Symfony/Bundle/WebServerBundle/Command/ServerLogCommand.php
@@ -106,7 +106,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 0130ed3d02866..99413dd1167bf 100644
--- a/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php
+++ b/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php
@@ -59,7 +59,7 @@ private function getManifestPath(string $path): ?string
$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 79ecf3ce995d9..25ce2d923570f 100644
--- a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
@@ -13,7 +13,6 @@
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;
@@ -117,10 +116,12 @@ public static function createSystemCache($namespace, $defaultLifetime, $version,
return $opcache;
}
+ if (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && !filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN)) {
+ return $opcache;
+ }
+
$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 63e97a8d0261e..05b8fc9c4be6e 100644
--- a/src/Symfony/Component/Cache/Adapter/ChainAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/ChainAdapter.php
@@ -51,6 +51,9 @@ public function __construct(array $adapters, int $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/FilesystemTagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/FilesystemTagAwareAdapter.php
index d9a1ad39ac6ef..2ce9eeb977fc5 100644
--- a/src/Symfony/Component/Cache/Adapter/FilesystemTagAwareAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/FilesystemTagAwareAdapter.php
@@ -107,7 +107,7 @@ protected function doSave(array $values, ?int $lifetime, array $addTagData = [],
$file = $this->getFile($id);
- if (!@symlink($file, $this->getFile($id, true, $tagFolder))) {
+ if (!@symlink($file, $tagLink = $this->getFile($id, true, $tagFolder)) && !is_link($tagLink)) {
@unlink($file);
$failed[] = $id;
}
diff --git a/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php b/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php
index 7e9dac803c32d..c296c8847c5cf 100644
--- a/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php
@@ -247,15 +247,11 @@ public function prune()
*/
public function reset()
{
- if (!$this->pool instanceof ResetInterface) {
- return;
- }
- $event = $this->start(__FUNCTION__);
- try {
+ if ($this->pool instanceof ResetInterface) {
$this->pool->reset();
- } finally {
- $event->end = microtime(true);
}
+
+ $this->clearCalls();
}
/**
diff --git a/src/Symfony/Component/Cache/DependencyInjection/CacheCollectorPass.php b/src/Symfony/Component/Cache/DependencyInjection/CacheCollectorPass.php
index 6193d34798757..b534e5dc8a7fb 100644
--- a/src/Symfony/Component/Cache/DependencyInjection/CacheCollectorPass.php
+++ b/src/Symfony/Component/Cache/DependencyInjection/CacheCollectorPass.php
@@ -46,27 +46,38 @@ public function process(ContainerBuilder $container)
return;
}
- $collectorDefinition = $container->getDefinition($this->dataCollectorCacheId);
foreach ($container->findTaggedServiceIds($this->cachePoolTag) as $id => $attributes) {
- $definition = $container->getDefinition($id);
- if ($definition->isAbstract()) {
- continue;
+ $this->addToCollector($id, $container);
+
+ if (($attributes[0]['name'] ?? $id) !== $id) {
+ $this->addToCollector($attributes[0]['name'], $container);
}
+ }
+ }
- $recorder = new Definition(is_subclass_of($definition->getClass(), TagAwareAdapterInterface::class) ? TraceableTagAwareAdapter::class : TraceableAdapter::class);
- $recorder->setTags($definition->getTags());
+ private function addToCollector(string $id, ContainerBuilder $container)
+ {
+ $definition = $container->getDefinition($id);
+ if ($definition->isAbstract()) {
+ return;
+ }
+
+ $collectorDefinition = $container->getDefinition($this->dataCollectorCacheId);
+ $recorder = new Definition(is_subclass_of($definition->getClass(), TagAwareAdapterInterface::class) ? TraceableTagAwareAdapter::class : TraceableAdapter::class);
+ $recorder->setTags($definition->getTags());
+ if (!$definition->isPublic() || !$definition->isPrivate()) {
$recorder->setPublic($definition->isPublic());
- $recorder->setArguments([new Reference($innerId = $id.$this->cachePoolRecorderInnerSuffix)]);
+ }
+ $recorder->setArguments([new Reference($innerId = $id.$this->cachePoolRecorderInnerSuffix)]);
- $definition->setTags([]);
- $definition->setPublic(false);
+ $definition->setTags([]);
+ $definition->setPublic(false);
- $container->setDefinition($innerId, $definition);
- $container->setDefinition($id, $recorder);
+ $container->setDefinition($innerId, $definition);
+ $container->setDefinition($id, $recorder);
- // Tell the collector to add the new instance
- $collectorDefinition->addMethodCall('addInstance', [$id, new Reference($id)]);
- $collectorDefinition->setPublic(false);
- }
+ // Tell the collector to add the new instance
+ $collectorDefinition->addMethodCall('addInstance', [$id, new Reference($id)]);
+ $collectorDefinition->setPublic(false);
}
}
diff --git a/src/Symfony/Component/Cache/Tests/DependencyInjection/CacheCollectorPassTest.php b/src/Symfony/Component/Cache/Tests/DependencyInjection/CacheCollectorPassTest.php
index 7e77491de867b..8aff19fc3e14b 100644
--- a/src/Symfony/Component/Cache/Tests/DependencyInjection/CacheCollectorPassTest.php
+++ b/src/Symfony/Component/Cache/Tests/DependencyInjection/CacheCollectorPassTest.php
@@ -13,6 +13,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
+use Symfony\Component\Cache\Adapter\PhpArrayAdapter;
use Symfony\Component\Cache\Adapter\TagAwareAdapter;
use Symfony\Component\Cache\Adapter\TraceableAdapter;
use Symfony\Component\Cache\Adapter\TraceableTagAwareAdapter;
@@ -34,16 +35,29 @@ public function testProcess()
->addArgument(new Reference('fs'))
->addTag('cache.pool');
+ $container
+ ->register('.php.inner', PhpArrayAdapter::class)
+ ->addTag('cache.pool', ['name' => 'php']);
+ $container
+ ->register('php', TagAwareAdapter::class)
+ ->addArgument(new Reference('.php.inner'));
+
$collector = $container->register('data_collector.cache', CacheDataCollector::class);
(new CacheCollectorPass())->process($container);
$this->assertEquals([
['addInstance', ['fs', new Reference('fs')]],
['addInstance', ['tagged_fs', new Reference('tagged_fs')]],
+ ['addInstance', ['.php.inner', new Reference('.php.inner')]],
+ ['addInstance', ['php', new Reference('php')]],
], $collector->getMethodCalls());
$this->assertSame(TraceableAdapter::class, $container->findDefinition('fs')->getClass());
$this->assertSame(TraceableTagAwareAdapter::class, $container->getDefinition('tagged_fs')->getClass());
+
+ $this->assertSame(TraceableAdapter::class, $container->findDefinition('.php.inner')->getClass());
+ $this->assertSame(TraceableTagAwareAdapter::class, $container->getDefinition('php')->getClass());
+
$this->assertFalse($collector->isPublic(), 'The "data_collector.cache" should be private after processing');
}
}
diff --git a/src/Symfony/Component/Cache/Tests/Traits/TagAwareTestTrait.php b/src/Symfony/Component/Cache/Tests/Traits/TagAwareTestTrait.php
index 7f1544af5b41d..8b687b5a7564c 100644
--- a/src/Symfony/Component/Cache/Tests/Traits/TagAwareTestTrait.php
+++ b/src/Symfony/Component/Cache/Tests/Traits/TagAwareTestTrait.php
@@ -155,4 +155,42 @@ public function testGetMetadata()
$i = $pool->getItem('k');
$this->assertSame(['foo' => 'foo'], $i->getMetadata()[CacheItem::METADATA_TAGS]);
}
+
+ public function testRefreshAfterExpires()
+ {
+ $pool = $this->createCachePool();
+ $pool->clear();
+
+ $cacheItem = $pool->getItem('test');
+
+ $this->assertFalse($cacheItem->isHit());
+
+ // write cache with expires
+ $cacheItem->set('test');
+ $cacheItem->tag('1234');
+ $cacheItem->expiresAfter(1);
+
+ $pool->save($cacheItem);
+
+ $cacheItem = $pool->getItem('test');
+ $this->assertTrue($cacheItem->isHit());
+
+ // wait until expired
+ sleep(2);
+
+ // item should not longer be a hit
+ $cacheItem = $pool->getItem('test');
+ $this->assertFalse($cacheItem->isHit());
+
+ // update expired item
+ $cacheItem->set('test');
+ $cacheItem->tag('1234');
+ $cacheItem->expiresAfter(1);
+
+ $pool->save($cacheItem);
+
+ // item should be again a hit
+ $cacheItem = $pool->getItem('test');
+ $this->assertTrue($cacheItem->isHit());
+ }
}
diff --git a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php
index 507bc1fbfb123..ca26f7e4ff7fc 100644
--- a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php
+++ b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php
@@ -303,7 +303,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()));
}
private function getClient(): \Memcached
diff --git a/src/Symfony/Component/Config/Definition/BaseNode.php b/src/Symfony/Component/Config/Definition/BaseNode.php
index ddf2f1155d6ba..edfb46e628347 100644
--- a/src/Symfony/Component/Config/Definition/BaseNode.php
+++ b/src/Symfony/Component/Config/Definition/BaseNode.php
@@ -438,7 +438,7 @@ final public function finalize($value)
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/Config/Tests/Resource/DirectoryResourceTest.php b/src/Symfony/Component/Config/Tests/Resource/DirectoryResourceTest.php
index aaadc8c4f430b..fbaed34a212a6 100644
--- a/src/Symfony/Component/Config/Tests/Resource/DirectoryResourceTest.php
+++ b/src/Symfony/Component/Config/Tests/Resource/DirectoryResourceTest.php
@@ -66,7 +66,7 @@ public function testGetPattern()
public function testResourceDoesNotExist()
{
$this->expectException('InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/The directory ".*" does not exist./');
+ $this->expectExceptionMessageMatches('/The directory ".*" does not exist./');
new DirectoryResource('/____foo/foobar'.mt_rand(1, 999999));
}
diff --git a/src/Symfony/Component/Config/Tests/Resource/FileResourceTest.php b/src/Symfony/Component/Config/Tests/Resource/FileResourceTest.php
index f85a820fb95bb..e3a45566c2617 100644
--- a/src/Symfony/Component/Config/Tests/Resource/FileResourceTest.php
+++ b/src/Symfony/Component/Config/Tests/Resource/FileResourceTest.php
@@ -56,7 +56,7 @@ public function testToString()
public function testResourceDoesNotExist()
{
$this->expectException('InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/The file ".*" does not exist./');
+ $this->expectExceptionMessageMatches('/The file ".*" does not exist./');
new FileResource('/____foo/foobar'.mt_rand(1, 999999));
}
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
index c56feafbcfb03..75813ba4976b0 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
@@ -153,7 +153,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/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
index 982472f5adf07..4a84cd1a24f11 100644
--- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
+++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
@@ -1700,7 +1700,11 @@ private function dumpValue($value, bool $interpolate = true): string
if (!$v) {
continue;
}
- $definition = $this->container->findDefinition($id = (string) $v);
+ $id = (string) $v;
+ while ($this->container->hasAlias($id)) {
+ $id = (string) $this->container->getAlias($id);
+ }
+ $definition = $this->container->getDefinition($id);
$load = !($definition->hasErrors() && $e = $definition->getErrors()) ? $this->asFiles && !$this->inlineFactories && !$this->isHotPath($definition) : reset($e);
$serviceMap .= sprintf("\n %s => [%s, %s, %s, %s],",
$this->export($k),
diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/AbstractServiceConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/AbstractServiceConfigurator.php
index 9d3305e8ee08c..2257edaef6daf 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/AbstractServiceConfigurator.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/AbstractServiceConfigurator.php
@@ -42,7 +42,7 @@ public function __destruct()
/**
* Registers a service.
*/
- final public function set(string $id, string $class = null): ServiceConfigurator
+ final public function set(?string $id, string $class = null): ServiceConfigurator
{
$this->__destruct();
diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/AliasConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/AliasConfigurator.php
index cb00f58c049d4..8ac635758bb74 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/AliasConfigurator.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/AliasConfigurator.php
@@ -20,6 +20,7 @@ class AliasConfigurator extends AbstractServiceConfigurator
{
const FACTORY = 'alias';
+ use Traits\DeprecateTrait;
use Traits\PublicTrait;
public function __construct(ServicesConfigurator $parent, Alias $alias)
diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/InlineServiceConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/InlineServiceConfigurator.php
index 362b374e55970..ea5db9778bad8 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/InlineServiceConfigurator.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/InlineServiceConfigurator.php
@@ -29,6 +29,10 @@ class InlineServiceConfigurator extends AbstractConfigurator
use Traits\ParentTrait;
use Traits\TagTrait;
+ private $id = '[inline]';
+ private $allowParent = true;
+ private $path = null;
+
public function __construct(Definition $definition)
{
$this->definition = $definition;
diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/PrototypeConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/PrototypeConfigurator.php
index 3cd56e0f19a48..1b740cee74461 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, string $namespace, string $resource, bool $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 f0fdde81c33a4..237099d08cd7e 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServicesConfigurator.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServicesConfigurator.php
@@ -83,13 +83,14 @@ final public function set(?string $id, string $class = null): ServiceConfigurato
$id = sprintf('.%d_%s', ++$this->anonymousCount, preg_replace('/^.*\\\\/', '', $class).'~'.$this->anonymousHash);
$definition->setPublic(false);
- } else {
- $definition->setPublic($defaults->isPublic());
+ } elseif (!$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(), $this->path);
@@ -103,7 +104,10 @@ final public function set(?string $id, string $class = null): ServiceConfigurato
final public function alias(string $id, string $referencedId): AliasConfigurator
{
$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 a31959e456552..b35952e4522fb 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
@@ -299,7 +299,7 @@ private function parseDefaults(array &$content, string $file): array
private function isUsingShortSyntax(array $service): bool
{
foreach ($service as $key => $value) {
- if (\is_string($key) && ('' === $key || '$' !== $key[0])) {
+ if (\is_string($key) && ('' === $key || ('$' !== $key[0] && false === strpos($key, '\\')))) {
return false;
}
}
@@ -345,7 +345,7 @@ private function parseDefinition(string $id, $service, string $file, array $defa
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']);
@@ -690,7 +690,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);
}
return $this->validate($configuration, $file);
@@ -763,7 +763,7 @@ private function resolveServices($value, string $file, bool $isParameter = false
if (\is_array($argument) && isset($argument['tag']) && $argument['tag']) {
if ($diff = array_diff(array_keys($argument), ['tag', 'index_by', 'default_index_method', 'default_priority_method'])) {
- throw new InvalidArgumentException(sprintf('"!%s" tag contains unsupported key "%s"; supported ones are "tag", "index_by", "default_index_method", and "default_priority_method".', $value->getTag(), implode('"", "', $diff)));
+ throw new InvalidArgumentException(sprintf('"!%s" tag contains unsupported key "%s"; supported ones are "tag", "index_by", "default_index_method", and "default_priority_method".', $value->getTag(), implode('", "', $diff)));
}
$argument = new TaggedIteratorArgument($argument['tag'], $argument['index_by'] ?? null, $argument['default_index_method'] ?? null, $forLocator, $argument['default_priority_method'] ?? null);
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php
index 77b1969f60b1c..d8e7d62d478f4 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php
@@ -88,6 +88,7 @@ public function testNoAttributes()
CustomDefinition::class => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
'bar' => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, 'bar')),
'baz' => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE, 'baz')),
+ 'late_alias' => new ServiceClosureArgument(new TypedReference(TestDefinition1::class, TestDefinition1::class, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, 'late_alias')),
];
$this->assertEquals($expected, $container->getDefinition((string) $locator->getFactory()[0])->getArgument(0));
@@ -118,6 +119,7 @@ public function testWithAttributes()
CustomDefinition::class => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
'bar' => new ServiceClosureArgument(new TypedReference('bar', CustomDefinition::class, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, 'bar')),
'baz' => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE, 'baz')),
+ 'late_alias' => new ServiceClosureArgument(new TypedReference(TestDefinition1::class, TestDefinition1::class, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, 'late_alias')),
];
$this->assertEquals($expected, $container->getDefinition((string) $locator->getFactory()[0])->getArgument(0));
@@ -260,6 +262,7 @@ public function testBinding()
CustomDefinition::class => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
'bar' => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, 'bar')),
'baz' => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE, 'baz')),
+ 'late_alias' => new ServiceClosureArgument(new TypedReference(TestDefinition1::class, TestDefinition1::class, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, 'late_alias')),
];
$this->assertEquals($expected, $container->getDefinition((string) $locator->getFactory()[0])->getArgument(0));
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php
index dfe37445d4e88..dd58f90990310 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php
@@ -400,7 +400,7 @@ protected function process(ContainerBuilder $container)
public function testProcessDetectsChildDefinitionIndirectCircularReference()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException');
- $this->expectExceptionMessageRegExp('/^Circular reference detected for service "c", path: "c -> b -> a -> c"./');
+ $this->expectExceptionMessageMatches('/^Circular reference detected for service "c", path: "c -> b -> a -> c"./');
$container = new ContainerBuilder();
$container->register('a');
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php
index ebd8f3df1024a..3acfbed776f1f 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php
@@ -233,7 +233,7 @@ public function testProcessForAutoconfiguredCalls()
public function testProcessThrowsExceptionForArguments()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/Autoconfigured instanceof for type "PHPUnit[\\\\_]Framework[\\\\_]TestCase" defines arguments but these are not supported and should be removed\./');
+ $this->expectExceptionMessageMatches('/Autoconfigured instanceof for type "PHPUnit[\\\\_]Framework[\\\\_]TestCase" defines arguments but these are not supported and should be removed\./');
$container = new ContainerBuilder();
$container->registerForAutoconfiguration(parent::class)
->addArgument('bar');
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ValidateEnvPlaceholdersPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ValidateEnvPlaceholdersPassTest.php
index 457c7a7e5cf03..8b50689dc0f5b 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ValidateEnvPlaceholdersPassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ValidateEnvPlaceholdersPassTest.php
@@ -105,7 +105,7 @@ public function testInvalidEnvInConfig()
public function testNulledEnvInConfig()
{
$this->expectException('Symfony\Component\Config\Definition\Exception\InvalidTypeException');
- $this->expectExceptionMessageRegexp('/^Invalid type for path "env_extension\.int_node"\. Expected "?int"?, but got (NULL|"null")\.$/');
+ $this->expectExceptionMessageMatches('/^Invalid type for path "env_extension\.int_node"\. Expected "?int"?, but got (NULL|"null")\.$/');
$container = new ContainerBuilder();
$container->setParameter('env(NULLED)', null);
$container->registerExtension(new EnvExtension());
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
index 60f1ab1d4f070..51a88705d6fe6 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
@@ -19,6 +19,8 @@
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocator as ArgumentServiceLocator;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
@@ -35,6 +37,7 @@
use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition;
use Symfony\Component\DependencyInjection\Tests\Fixtures\ScalarFactory;
use Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator;
+use Symfony\Component\DependencyInjection\Tests\Fixtures\TestDefinition1;
use Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber;
use Symfony\Component\DependencyInjection\TypedReference;
use Symfony\Component\DependencyInjection\Variable;
@@ -906,6 +909,20 @@ public function testServiceSubscriber()
$container->register(CustomDefinition::class, CustomDefinition::class)
->setPublic(false);
+
+ $container->register(TestDefinition1::class, TestDefinition1::class)->setPublic(true);
+
+ $container->addCompilerPass(new class() implements CompilerPassInterface {
+ /**
+ * {@inheritdoc}
+ */
+ public function process(ContainerBuilder $container)
+ {
+ $container->setDefinition('late_alias', new Definition(TestDefinition1::class));
+ $container->setAlias(TestDefinition1::class, 'late_alias');
+ }
+ }, PassConfig::TYPE_AFTER_REMOVING);
+
$container->compile();
$dumper = new PhpDumper($container);
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriber.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriber.php
index 6876fc5cca53d..f4a4bdda6451e 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriber.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriber.php
@@ -22,6 +22,7 @@ public static function getSubscribedServices(): array
'?'.CustomDefinition::class,
'bar' => CustomDefinition::class,
'baz' => '?'.CustomDefinition::class,
+ 'late_alias' => TestDefinition1::class,
];
}
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/anonymous.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/anonymous.php
index 112b162bcaca9..65605bcccff89 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/anonymous.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/anonymous.php
@@ -7,7 +7,7 @@
use Symfony\Component\DependencyInjection\Tests\Fixtures\StdClassDecorator;
return function (ContainerConfigurator $c) {
- $s = $c->services();
+ $s = $c->services()->defaults()->public();
$s->set('decorated', stdClass::class);
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/basic.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/basic.php
index a9e250b9213a1..89132620ec3c8 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/basic.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/basic.php
@@ -5,7 +5,7 @@
use App\BarService;
return function (ContainerConfigurator $c) {
- $s = $c->services();
+ $s = $c->services()->defaults()->public();
$s->set(BarService::class)
->args([inline('FooClass')]);
};
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/child.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/child.php
index 8a5f2431d0abf..d2735bcfb1bfb 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/child.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/child.php
@@ -6,12 +6,14 @@
return function (ContainerConfigurator $c) {
$c->services()
- ->set('bar', 'Class1')
+ ->set('bar', 'Class1')->public()
->set(BarService::class)
+ ->public()
->abstract(true)
->lazy()
->set('foo')
->parent(BarService::class)
+ ->public()
->decorate('bar', 'b', 1)
->args([ref('b')])
->class('Class2')
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..2b389b694590a 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/defaults.expected.yml
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/defaults.expected.yml
@@ -18,7 +18,7 @@ services:
arguments: ['@bar']
bar:
class: Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo
- public: false
+ public: true
tags:
- { name: t, a: b }
autowire: true
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/defaults.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/defaults.php
index 2889d3fbb6400..b04413d2102f3 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/defaults.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/defaults.php
@@ -14,7 +14,7 @@
->autowire()
->tag('t', ['a' => 'b'])
->bind(Foo::class, ref('bar'))
- ->private();
+ ->public();
$s->set(Foo::class)->args([ref('bar')])->public();
$s->set('bar', Foo::class)->call('setFoo')->autoconfigure(false);
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/instanceof.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/instanceof.php
index 0d6aac7a0051e..d9f62a67ec9a8 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/instanceof.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/instanceof.php
@@ -6,7 +6,7 @@
use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype;
return function (ContainerConfigurator $c) {
- $s = $c->services();
+ $s = $c->services()->defaults()->public();
$s->instanceof(Prototype\Foo::class)
->property('p', 0)
->call('setFoo', [ref('foo')])
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/lazy_fqcn.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/lazy_fqcn.php
index 7cde4ef2d0699..d23f995993424 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/lazy_fqcn.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/lazy_fqcn.php
@@ -3,6 +3,6 @@
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
return function (ContainerConfigurator $c) {
- $di = $c->services();
+ $di = $c->services()->defaults()->public();
$di->set('foo', 'stdClass')->lazy('SomeInterface');
};
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/object.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/object.php
index daf4682bf7efa..59c4d43fd2b11 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/object.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/object.php
@@ -7,7 +7,7 @@
return new class() {
public function __invoke(ContainerConfigurator $c)
{
- $s = $c->services();
+ $s = $c->services()->defaults()->public();
$s->set(BarService::class)
->args([inline('FooClass')]);
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/php7.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/php7.php
index 7711624e6f0f6..98134fb230409 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/php7.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/php7.php
@@ -9,7 +9,7 @@
('foo', 'Foo')
('bar', 'Bar')
;
- $c->services()
+ $c->services()->defaults()->public()
(Foo::class)
->arg('$bar', ref('bar'))
->public()
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype.php
index 6a7d859df1fd6..f5baa0f642738 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype.php
@@ -8,6 +8,7 @@
$di = $c->services()->defaults()
->tag('baz');
$di->load(Prototype::class.'\\', '../Prototype')
+ ->public()
->autoconfigure()
->exclude('../Prototype/{OtherDir,BadClasses,SinglyImplementedInterface}')
->factory('f')
@@ -17,6 +18,6 @@
->autoconfigure(false)
->tag('foo')
->parent('foo');
- $di->set('foo')->lazy()->abstract();
+ $di->set('foo')->lazy()->abstract()->public();
$di->get(Prototype\Foo::class)->lazy(false);
};
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype_array.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype_array.php
index 501baa3c10ab7..026ee6c1ab693 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype_array.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype_array.php
@@ -8,6 +8,7 @@
$di = $c->services()->defaults()
->tag('baz');
$di->load(Prototype::class.'\\', '../Prototype')
+ ->public()
->autoconfigure()
->exclude(['../Prototype/OtherDir', '../Prototype/BadClasses', '../Prototype/SinglyImplementedInterface'])
->factory('f')
@@ -17,6 +18,6 @@
->autoconfigure(false)
->tag('foo')
->parent('foo');
- $di->set('foo')->lazy()->abstract();
+ $di->set('foo')->lazy()->abstract()->public();
$di->get(Prototype\Foo::class)->lazy(false);
};
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services9.php
index 7c070ef64f450..8691d48efe257 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services9.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services9.php
@@ -16,8 +16,7 @@
$p->set('foo_class', FooClass::class)
->set('foo', 'bar');
- $s = $c->services()->defaults()
- ->public();
+ $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)
@@ -127,12 +126,10 @@
->tag('foo');
$s->set('tagged_iterator', 'Bar')
- ->public()
->args([tagged_iterator('foo')]);
$s->set('runtime_error', 'stdClass')
- ->args([new Reference('errored_definition', ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE)])
- ->public();
+ ->args([new Reference('errored_definition', ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE)]);
$s->set('errored_definition', 'stdClass')->private();
$s->alias('alias_for_foo', 'foo')->private()->public();
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php
index e184fad7bdf0c..caec312fdda41 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php
@@ -27,9 +27,11 @@ public function __construct()
$this->methodMap = [
'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber' => 'getTestServiceSubscriberService',
'foo_service' => 'getFooServiceService',
+ 'late_alias' => 'getLateAliasService',
+ ];
+ $this->aliases = [
+ 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestDefinition1' => 'late_alias',
];
-
- $this->aliases = [];
}
public function compile(): void
@@ -45,11 +47,13 @@ public function isCompiled(): bool
public function getRemovedIds(): array
{
return [
- '.service_locator.bPEFRiK' => true,
- '.service_locator.bPEFRiK.foo_service' => true,
+ '.service_locator.CpwjbIa' => true,
+ '.service_locator.CpwjbIa.foo_service' => true,
'Psr\\Container\\ContainerInterface' => true,
'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true,
'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => true,
+ 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestDefinition1' => true,
+ 'late_alias' => true,
];
}
@@ -75,14 +79,26 @@ protected function getFooServiceService()
'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber' => ['services', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber', 'getTestServiceSubscriberService', false],
'bar' => ['services', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber', 'getTestServiceSubscriberService', false],
'baz' => ['privates', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition', 'getCustomDefinitionService', false],
+ 'late_alias' => ['services', 'late_alias', 'getLateAliasService', false],
], [
'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition',
'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber',
'bar' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition',
'baz' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition',
+ 'late_alias' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestDefinition1',
]))->withContext('foo_service', $this));
}
+ /**
+ * Gets the public 'late_alias' shared service.
+ *
+ * @return \Symfony\Component\DependencyInjection\Tests\Fixtures\TestDefinition1
+ */
+ protected function getLateAliasService()
+ {
+ return $this->services['late_alias'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\TestDefinition1();
+ }
+
/**
* Gets the private 'Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition' shared service.
*
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_short_syntax.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_short_syntax.yml
new file mode 100644
index 0000000000000..16ddb6bdf241a
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_short_syntax.yml
@@ -0,0 +1,6 @@
+services:
+ foo_bar: [1, 2]
+
+ bar_foo:
+ $a: 'a'
+ App\Foo: 'foo'
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php
index 57ea37e6f4964..61971f408c477 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php
@@ -216,7 +216,7 @@ public function testMissingParentClass()
public function testRegisterClassesWithBadPrefix()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/Expected to find class "Symfony\\\Component\\\DependencyInjection\\\Tests\\\Fixtures\\\Prototype\\\Bar" in file ".+" while importing services from resource "Prototype\/Sub\/\*", but it was not found\! Check the namespace prefix used with the resource/');
+ $this->expectExceptionMessageMatches('/Expected to find class "Symfony\\\Component\\\DependencyInjection\\\Tests\\\Fixtures\\\Prototype\\\Bar" in file ".+" while importing services from resource "Prototype\/Sub\/\*", but it was not found\! Check the namespace prefix used with the resource/');
$container = new ContainerBuilder();
$loader = new TestFileLoader($container, new FileLocator(self::$fixturesPath.'/Fixtures'));
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
index 14ba4c7f6e567..e5868edad2cea 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
@@ -378,7 +378,7 @@ public function testParseTagsWithoutNameThrowsException()
public function testParseTagWithEmptyNameThrowsException()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/The tag name for service ".+" in .* must be a non-empty string/');
+ $this->expectExceptionMessageMatches('/The tag name for service ".+" in .* must be a non-empty string/');
$container = new ContainerBuilder();
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
$loader->load('tag_with_empty_name.xml');
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php
index f40fc22cc3452..fc87dd7168ccc 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php
@@ -50,7 +50,7 @@ public static function setUpBeforeClass(): void
public function testLoadUnExistFile()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/The file ".+" does not exist./');
+ $this->expectExceptionMessageMatches('/The file ".+" does not exist./');
$loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/ini'));
$r = new \ReflectionObject($loader);
$m = $r->getMethod('loadFile');
@@ -62,7 +62,7 @@ public function testLoadUnExistFile()
public function testLoadInvalidYamlFile()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/The file ".+" does not contain valid YAML./');
+ $this->expectExceptionMessageMatches('/The file ".+" does not contain valid YAML./');
$path = self::$fixturesPath.'/ini';
$loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator($path));
$r = new \ReflectionObject($loader);
@@ -205,6 +205,17 @@ public function testLoadServices()
$this->assertEquals(['decorated', 'decorated.pif-pouf', 5, ContainerInterface::IGNORE_ON_INVALID_REFERENCE], $services['decorator_service_with_name_and_priority_and_on_invalid']->getDecoratedService());
}
+ public function testLoadShortSyntax()
+ {
+ $container = new ContainerBuilder();
+ $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
+ $loader->load('services_short_syntax.yml');
+ $services = $container->getDefinitions();
+
+ $this->assertSame([1, 2], $services['foo_bar']->getArguments());
+ $this->assertSame(['$a' => 'a', 'App\Foo' => 'foo'], $services['bar_foo']->getArguments());
+ }
+
public function testDeprecatedAliases()
{
$container = new ContainerBuilder();
@@ -384,7 +395,7 @@ public function testLoadYamlOnlyWithKeys()
public function testTagWithEmptyNameThrowsException()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/The tag name for service ".+" in .+ must be a non-empty string/');
+ $this->expectExceptionMessageMatches('/The tag name for service ".+" in .+ must be a non-empty string/');
$loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('tag_name_empty_string.yml');
}
@@ -392,7 +403,7 @@ public function testTagWithEmptyNameThrowsException()
public function testTagWithNonStringNameThrowsException()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/The tag name for service ".+" in .+ must be a non-empty string/');
+ $this->expectExceptionMessageMatches('/The tag name for service ".+" in .+ must be a non-empty string/');
$loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('tag_name_no_string.yml');
}
@@ -493,7 +504,7 @@ public function testPrototypeWithNamespace()
public function testPrototypeWithNamespaceAndNoResource()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/A "resource" attribute must be set when the "namespace" attribute is set for service ".+" in .+/');
+ $this->expectExceptionMessageMatches('/A "resource" attribute must be set when the "namespace" attribute is set for service ".+" in .+/');
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('services_prototype_namespace_without_resource.yml');
@@ -621,7 +632,7 @@ public function testDecoratedServicesWithWrongOnInvalidSyntaxThrowsException()
public function testInvalidTagsWithDefaults()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/Parameter "tags" must be an array for service "Foo\\\Bar" in ".+services31_invalid_tags\.yml"\. Check your YAML syntax./');
+ $this->expectExceptionMessageMatches('/Parameter "tags" must be an array for service "Foo\\\Bar" in ".+services31_invalid_tags\.yml"\. Check your YAML syntax./');
$loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('services31_invalid_tags.yml');
}
@@ -711,7 +722,7 @@ public function testAnonymousServicesInInstanceof()
public function testAnonymousServicesWithAliases()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/Creating an alias using the tag "!service" is not allowed in ".+anonymous_services_alias\.yml"\./');
+ $this->expectExceptionMessageMatches('/Creating an alias using the tag "!service" is not allowed in ".+anonymous_services_alias\.yml"\./');
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('anonymous_services_alias.yml');
@@ -720,7 +731,7 @@ public function testAnonymousServicesWithAliases()
public function testAnonymousServicesInParameters()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/Using an anonymous service in a parameter is not allowed in ".+anonymous_services_in_parameters\.yml"\./');
+ $this->expectExceptionMessageMatches('/Using an anonymous service in a parameter is not allowed in ".+anonymous_services_in_parameters\.yml"\./');
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('anonymous_services_in_parameters.yml');
@@ -739,7 +750,7 @@ public function testAutoConfigureInstanceof()
public function testEmptyDefaultsThrowsClearException()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/Service "_defaults" key must be an array, "NULL" given in ".+bad_empty_defaults\.yml"\./');
+ $this->expectExceptionMessageMatches('/Service "_defaults" key must be an array, "NULL" given in ".+bad_empty_defaults\.yml"\./');
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('bad_empty_defaults.yml');
@@ -748,7 +759,7 @@ public function testEmptyDefaultsThrowsClearException()
public function testEmptyInstanceofThrowsClearException()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/Service "_instanceof" key must be an array, "NULL" given in ".+bad_empty_instanceof\.yml"\./');
+ $this->expectExceptionMessageMatches('/Service "_instanceof" key must be an array, "NULL" given in ".+bad_empty_instanceof\.yml"\./');
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('bad_empty_instanceof.yml');
@@ -757,7 +768,7 @@ public function testEmptyInstanceofThrowsClearException()
public function testUnsupportedKeywordThrowsException()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/^The configuration key "private" is unsupported for definition "bar"/');
+ $this->expectExceptionMessageMatches('/^The configuration key "private" is unsupported for definition "bar"/');
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('bad_keyword.yml');
@@ -766,7 +777,7 @@ public function testUnsupportedKeywordThrowsException()
public function testUnsupportedKeywordInServiceAliasThrowsException()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/^The configuration key "calls" is unsupported for the service "bar" which is defined as an alias/');
+ $this->expectExceptionMessageMatches('/^The configuration key "calls" is unsupported for the service "bar" which is defined as an alias/');
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('bad_alias.yml');
diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php
index 9c7b30dc225d9..ed8b6852a5dbc 100644
--- a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php
+++ b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php
@@ -231,7 +231,7 @@ public function testRegisterAfterEval($registerCallback)
public function testCallBadCallable()
{
$this->expectException('RuntimeException');
- $this->expectExceptionMessageRegExp('/Unable to call method "\w+" of object "\w+"./');
+ $this->expectExceptionMessageMatches('/Unable to call method "\w+" of object "\w+"./');
$el = new ExpressionLanguage();
$el->evaluate('foo.myfunction()', ['foo' => new \stdClass()]);
}
diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php
index 9054d7be3d336..85c997109a733 100644
--- a/src/Symfony/Component/Filesystem/Filesystem.php
+++ b/src/Symfony/Component/Filesystem/Filesystem.php
@@ -102,7 +102,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);
}
@@ -172,16 +172,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/Form/Extension/Core/Type/DateTimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php
index 5d936ffb6b6e3..c271d098086c7 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php
@@ -218,6 +218,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 e0885eae6bf33..81d4c632e1f87 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,44 +46,70 @@ 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);
+ }
+
$groupedConstraints = [];
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;
}
@@ -101,7 +129,7 @@ public function validate($form, Constraint $formConstraint)
}
foreach ($groupedConstraints as $group => $constraint) {
- $validator->atPath('data')->validate($form->getData(), $constraint, $group);
+ $validator->atPath('data')->validate($data, $constraint, $group);
}
}
} elseif (!$form->isSynchronized()) {
@@ -159,7 +187,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;
@@ -183,6 +211,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);
@@ -208,4 +240,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 b9cc334256174..867a5768aee6c 100644
--- a/src/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php
+++ b/src/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php
@@ -50,8 +50,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 d6073f3ed3b67..3981627e6b4b2 100644
--- a/src/Symfony/Component/Form/Form.php
+++ b/src/Symfony/Component/Form/Form.php
@@ -1056,7 +1056,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, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters());
+ throw new TransformationFailedException(sprintf('Unable to transform data for property path "%s": '.$exception->getMessage(), $this->getPropertyPath()), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters());
}
return $value;
@@ -1078,7 +1078,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, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters());
+ throw new TransformationFailedException(sprintf('Unable to reverse value for property path "%s": '.$exception->getMessage(), $this->getPropertyPath()), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters());
}
return $value;
@@ -1107,7 +1107,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, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters());
+ throw new TransformationFailedException(sprintf('Unable to transform value for property path "%s": '.$exception->getMessage(), $this->getPropertyPath()), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters());
}
return $value;
@@ -1131,7 +1131,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, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters());
+ throw new TransformationFailedException(sprintf('Unable to reverse value for property path "%s": '.$exception->getMessage(), $this->getPropertyPath()), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters());
}
return $value;
diff --git a/src/Symfony/Component/Form/ResolvedFormType.php b/src/Symfony/Component/Form/ResolvedFormType.php
index 02a06c85e1161..fdffc946a5eeb 100644
--- a/src/Symfony/Component/Form/ResolvedFormType.php
+++ b/src/Symfony/Component/Form/ResolvedFormType.php
@@ -96,7 +96,7 @@ public function createBuilder(FormFactoryInterface $factory, $name, array $optio
try {
$options = $this->getOptionsResolver()->resolve($options);
} catch (ExceptionInterface $e) {
- throw new $e(sprintf('An error has occurred resolving the options of the form "%s": %s.', \get_class($this->getInnerType()), $e->getMessage()), $e->getCode(), $e);
+ throw new $e(sprintf('An error has occurred resolving the options of the form "%s": '.$e->getMessage(), \get_class($this->getInnerType())), $e->getCode(), $e);
}
// Should be decoupled from the specific option at some point
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/CheckboxTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php
index 8391805b22666..e12347405dee9 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php
@@ -197,7 +197,7 @@ public function provideCustomFalseValues()
public function testDontAllowNonArrayFalseValues()
{
$this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException');
- $this->expectExceptionMessageRegExp('/"false_values" with value "invalid" is expected to be of type "array"/');
+ $this->expectExceptionMessageMatches('/"false_values" with value "invalid" is expected to be of type "array"/');
$this->factory->create(static::TESTED_TYPE, null, [
'false_values' => 'invalid',
]);
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 b0c4f3549252d..b9012bdc699e4 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php
@@ -495,6 +495,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 testSingleTextWidgetWithCustomNonHtml5Format()
{
$form = $this->factory->create(static::TESTED_TYPE, new \DateTime('2019-02-13 19:12:13'), [
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 267511d88ccd4..a70f65021680f 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 3f6294c53b3d6..782e1ed32749e 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php
@@ -444,8 +444,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());
@@ -799,6 +799,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();
@@ -821,7 +854,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 a920e3be5b3ac..0b1dab5485ecc 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
@@ -66,14 +70,71 @@ public function testGroupSequenceWithConstraintsOption()
->add('field', TextTypeTest::TESTED_TYPE, [
'constraints' => [
new Length(['min' => 10, 'groups' => ['First']] + $allowEmptyString),
- 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()
+ {
+ $allowEmptyString = property_exists(Length::class, 'allowEmptyString') ? ['allowEmptyString' => true] : [];
+
+ $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']] + $allowEmptyString),
+ ],
+ ])
+ ->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..383b7556d51b8 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,57 @@ public function testFieldConstraintsInvalidateFormIfFieldIsSubmitted()
$this->assertFalse($form->get('baz')->isValid());
}
- private function createForm($type)
+ public function testFieldsValidateInSequence()
+ {
+ $allowEmptyString = property_exists(Length::class, 'allowEmptyString') ? ['allowEmptyString' => true] : [];
+
+ $form = $this->createForm(FormType::class, null, [
+ 'validation_groups' => new GroupSequence(['group1', 'group2']),
+ ])
+ ->add('foo', TextType::class, [
+ 'constraints' => [new Length(['min' => 10, 'groups' => ['group1']] + $allowEmptyString)],
+ ])
+ ->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()
+ {
+ $allowEmptyString = property_exists(Length::class, 'allowEmptyString') ? ['allowEmptyString' => true] : [];
+
+ $form = $this->createForm(FormType::class, null, [
+ 'validation_groups' => new GroupSequence([['group1', 'group2'], 'group3']),
+ ])
+ ->add('foo', TextType::class, [
+ 'constraints' => [new Length(['min' => 10, 'groups' => ['group1']] + $allowEmptyString)],
+ ])
+ ->add('bar', TextType::class, [
+ 'constraints' => [new Length(['min' => 10, 'groups' => ['group2']] + $allowEmptyString)],
+ ])
+ ->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 +151,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/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php
index ddc7f9d9f351c..2fcba7902ebef 100644
--- a/src/Symfony/Component/HttpClient/CurlHttpClient.php
+++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php
@@ -141,12 +141,12 @@ public function request(string $method, string $url, array $options = []): Respo
CURLOPT_CERTINFO => $options['capture_peer_cert_chain'],
];
- if (1.0 === (float) $options['http_version']) {
+ if (\defined('CURL_VERSION_HTTP2') && (CURL_VERSION_HTTP2 & self::$curlVersion['features']) && ('https:' === $scheme || 2.0 === (float) $options['http_version'])) {
+ $curlopts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0;
+ } elseif (1.0 === (float) $options['http_version']) {
$curlopts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0;
- } elseif (1.1 === (float) $options['http_version'] || 'https:' !== $scheme) {
+ } elseif (1.1 === (float) $options['http_version']) {
$curlopts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1;
- } elseif (\defined('CURL_VERSION_HTTP2') && CURL_VERSION_HTTP2 & self::$curlVersion['features']) {
- $curlopts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0;
}
if (isset($options['auth_ntlm'])) {
diff --git a/src/Symfony/Component/HttpClient/HttpClientTrait.php b/src/Symfony/Component/HttpClient/HttpClientTrait.php
index e150e56a0c9c0..51856da9bfef4 100644
--- a/src/Symfony/Component/HttpClient/HttpClientTrait.php
+++ b/src/Symfony/Component/HttpClient/HttpClientTrait.php
@@ -343,11 +343,11 @@ private static function jsonEncode($value, int $flags = null, int $maxDepth = 51
try {
$value = json_encode($value, $flags | (\PHP_VERSION_ID >= 70300 ? JSON_THROW_ON_ERROR : 0), $maxDepth);
} catch (\JsonException $e) {
- throw new InvalidArgumentException(sprintf('Invalid value for "json" option: %s.', $e->getMessage()));
+ throw new InvalidArgumentException('Invalid value for "json" option: '.$e->getMessage());
}
if (\PHP_VERSION_ID < 70300 && JSON_ERROR_NONE !== json_last_error() && (false === $value || !($flags & JSON_PARTIAL_OUTPUT_ON_ERROR))) {
- throw new InvalidArgumentException(sprintf('Invalid value for "json" option: %s.', json_last_error_msg()));
+ throw new InvalidArgumentException('Invalid value for "json" option: '.json_last_error_msg());
}
return $value;
diff --git a/src/Symfony/Component/HttpClient/Response/ResponseTrait.php b/src/Symfony/Component/HttpClient/Response/ResponseTrait.php
index f49ca68338408..de4d7598b923e 100644
--- a/src/Symfony/Component/HttpClient/Response/ResponseTrait.php
+++ b/src/Symfony/Component/HttpClient/Response/ResponseTrait.php
@@ -153,11 +153,11 @@ public function toArray(bool $throw = true): array
try {
$content = json_decode($content, true, 512, JSON_BIGINT_AS_STRING | (\PHP_VERSION_ID >= 70300 ? JSON_THROW_ON_ERROR : 0));
} catch (\JsonException $e) {
- throw new JsonException(sprintf('%s for "%s".', $e->getMessage(), $this->getInfo('url')), $e->getCode());
+ throw new JsonException(sprintf($e->getMessage().' for "%s".', $this->getInfo('url')), $e->getCode());
}
if (\PHP_VERSION_ID < 70300 && JSON_ERROR_NONE !== json_last_error()) {
- throw new JsonException(sprintf('%s for "%s".', json_last_error_msg(), $this->getInfo('url')), json_last_error());
+ throw new JsonException(sprintf(json_last_error_msg().' for "%s".', $this->getInfo('url')), json_last_error());
}
if (!\is_array($content)) {
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/AbstractSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/AbstractSessionHandler.php
index a196833cbf5d3..c4fa1fae206fa 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 54933d953af3a..b6cce8165edb8 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php
@@ -215,8 +215,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) {
@@ -225,10 +227,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 ace6249394365..4cb0b2dab3885 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php
@@ -120,6 +120,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 feb69e54ac9fb..b3e21e5f26c21 100644
--- a/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php
+++ b/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php
@@ -64,7 +64,7 @@ public function getController(Request $request)
}
if (!\is_callable($controller)) {
- throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable. %s.', $request->getPathInfo(), $this->getControllerError($controller)));
+ throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable: '.$this->getControllerError($controller), $request->getPathInfo()));
}
return $controller;
@@ -72,7 +72,7 @@ public function getController(Request $request)
if (\is_object($controller)) {
if (!\is_callable($controller)) {
- throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable. %s.', $request->getPathInfo(), $this->getControllerError($controller)));
+ throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable: '.$this->getControllerError($controller), $request->getPathInfo()));
}
return $controller;
@@ -85,11 +85,11 @@ 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);
}
if (!\is_callable($callable)) {
- throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable. %s.', $request->getPathInfo(), $this->getControllerError($callable)));
+ throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable: '.$this->getControllerError($callable), $request->getPathInfo()));
}
return $callable;
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php
index 9314e432e150f..3bce904ccd2d2 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php
@@ -179,7 +179,7 @@ private function sanitizeLogs(array $logs)
continue;
}
- $message = $log['message'];
+ $message = '_'.$log['message'];
$exception = $log['context']['exception'];
if ($exception instanceof SilencedErrorContext) {
diff --git a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php
index dcb54ea891c4a..0e672a299dd41 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php
@@ -15,6 +15,7 @@
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleEvent;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
+use Symfony\Component\Debug\ErrorHandler as LegacyErrorHandler;
use Symfony\Component\Debug\Exception\FatalThrowableError;
use Symfony\Component\ErrorHandler\ErrorHandler;
use Symfony\Component\EventDispatcher\Event;
@@ -79,7 +80,7 @@ public function configure(Event $event = null)
restore_exception_handler();
if ($this->logger || null !== $this->throwAt) {
- if ($handler instanceof ErrorHandler) {
+ if ($handler instanceof ErrorHandler || $handler instanceof LegacyErrorHandler) {
if ($this->logger) {
$handler->setDefaultLogger($this->logger, $this->levels);
if (\is_array($this->levels)) {
@@ -138,7 +139,7 @@ public function configure(Event $event = null)
}
}
if ($this->exceptionHandler) {
- if ($handler instanceof ErrorHandler) {
+ if ($handler instanceof ErrorHandler || $handler instanceof LegacyErrorHandler) {
$handler->setExceptionHandler($this->exceptionHandler);
}
$this->exceptionHandler = null;
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index 89198bd7cffcc..9cb3a034d6961 100644
--- a/src/Symfony/Component/HttpKernel/Kernel.php
+++ b/src/Symfony/Component/HttpKernel/Kernel.php
@@ -76,11 +76,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
private static $freshCache = [];
- const VERSION = '4.4.7';
- const VERSION_ID = 40407;
+ const VERSION = '4.4.8';
+ const VERSION_ID = 40408;
const MAJOR_VERSION = 4;
const MINOR_VERSION = 4;
- const RELEASE_VERSION = 7;
+ const RELEASE_VERSION = 8;
const EXTRA_VERSION = '';
const END_OF_MAINTENANCE = '11/2022';
diff --git a/src/Symfony/Component/HttpKernel/Log/Logger.php b/src/Symfony/Component/HttpKernel/Log/Logger.php
index 6ea3b464f0d5e..e3badc3676e62 100644
--- a/src/Symfony/Component/HttpKernel/Log/Logger.php
+++ b/src/Symfony/Component/HttpKernel/Log/Logger.php
@@ -79,7 +79,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));
}
private function format(string $level, string $message, array $context): string
diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php
index 014b21e83aff3..d5ac4ad5c2609 100644
--- a/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php
@@ -173,17 +173,17 @@ public function getUndefinedControllers()
['foo', \Error::class, 'Class \'foo\' not found'],
['oof::bar', \Error::class, 'Class \'oof\' not found'],
[['oof', 'bar'], \Error::class, 'Class \'oof\' not found'],
- ['Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::staticsAction', \InvalidArgumentException::class, '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::class, '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::class, '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::class, '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', \InvalidArgumentException::class, 'The controller for URI "/" is not callable. Controller class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" cannot be called without a method name. You need to implement "__invoke" or use one of the available methods: "publicAction", "staticAction".'],
- [[$controller, 'staticsAction'], \InvalidArgumentException::class, 'The controller for URI "/" is not callable. Expected method "staticsAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest", did you mean "staticAction"?'],
- [[$controller, 'privateAction'], \InvalidArgumentException::class, 'The controller for URI "/" is not callable. Method "privateAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'],
- [[$controller, 'protectedAction'], \InvalidArgumentException::class, 'The controller for URI "/" is not callable. Method "protectedAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'],
- [[$controller, 'undefinedAction'], \InvalidArgumentException::class, 'The controller for URI "/" is not callable. Expected method "undefinedAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest". Available methods: "publicAction", "staticAction"'],
- [$controller, \InvalidArgumentException::class, 'The controller for URI "/" is not callable. Controller class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" cannot be called without a method name. You need to implement "__invoke" or use one of the available methods: "publicAction", "staticAction".'],
- [['a' => 'foo', 'b' => 'bar'], \InvalidArgumentException::class, 'The controller for URI "/" is not callable. Invalid array callable, expected [controller, method].'],
+ ['Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::staticsAction', \InvalidArgumentException::class, '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::class, '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::class, '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::class, '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', \InvalidArgumentException::class, 'The controller for URI "/" is not callable: Controller class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" cannot be called without a method name. You need to implement "__invoke" or use one of the available methods: "publicAction", "staticAction".'],
+ [[$controller, 'staticsAction'], \InvalidArgumentException::class, 'The controller for URI "/" is not callable: Expected method "staticsAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest", did you mean "staticAction"?'],
+ [[$controller, 'privateAction'], \InvalidArgumentException::class, 'The controller for URI "/" is not callable: Method "privateAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'],
+ [[$controller, 'protectedAction'], \InvalidArgumentException::class, 'The controller for URI "/" is not callable: Method "protectedAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'],
+ [[$controller, 'undefinedAction'], \InvalidArgumentException::class, 'The controller for URI "/" is not callable: Expected method "undefinedAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest". Available methods: "publicAction", "staticAction"'],
+ [$controller, \InvalidArgumentException::class, 'The controller for URI "/" is not callable: Controller class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" cannot be called without a method name. You need to implement "__invoke" or use one of the available methods: "publicAction", "staticAction".'],
+ [['a' => 'foo', 'b' => 'bar'], \InvalidArgumentException::class, 'The controller for URI "/" is not callable: Invalid array callable, expected [controller, method].'],
];
}
diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php
index adfba5d4220ee..9c175397fced7 100644
--- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php
@@ -176,13 +176,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 573ab0ce99475..5531a7c435321 100644
--- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Collection.php
+++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Collection.php
@@ -53,7 +53,7 @@ public function count()
foreach ($searches as $search) {
$searchCount = ldap_count_entries($con, $search);
if (false === $searchCount) {
- throw new LdapException(sprintf('Error while retrieving entry count: %s.', ldap_error($con)));
+ throw new LdapException('Error while retrieving entry count: '.ldap_error($con));
}
$count += $searchCount;
}
@@ -76,7 +76,7 @@ public function getIterator()
$current = ldap_first_entry($con, $search);
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);
@@ -123,7 +123,7 @@ private function getSingleEntry($con, $current): Entry
$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);
@@ -131,7 +131,7 @@ private function getSingleEntry($con, $current): Entry
$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 53fc5a86025d4..b25a6eb447c7e 100644
--- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php
+++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php
@@ -140,11 +140,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 20055c2f3b24f..c08e2c1550303 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()));
}
}
@@ -79,7 +79,7 @@ public function addAttributeValues(Entry $entry, string $attribute, array $value
$con = $this->getConnectionResource();
if (!@ldap_mod_add($con, $entry->getDn(), [$attribute => $values])) {
- throw new LdapException(sprintf('Could not add values to entry "%s", attribute %s: %s.', $entry->getDn(), $attribute, ldap_error($con)));
+ throw new LdapException(sprintf('Could not add values to entry "%s", attribute %s: '.ldap_error($con), $entry->getDn(), $attribute));
}
}
@@ -94,7 +94,7 @@ public function removeAttributeValues(Entry $entry, string $attribute, array $va
$con = $this->getConnectionResource();
if (!@ldap_mod_del($con, $entry->getDn(), [$attribute => $values])) {
- throw new LdapException(sprintf('Could not remove values from entry "%s", attribute %s: %s.', $entry->getDn(), $attribute, ldap_error($con)));
+ throw new LdapException(sprintf('Could not remove values from entry "%s", attribute %s: '.ldap_error($con), $entry->getDn(), $attribute));
}
}
@@ -106,7 +106,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));
}
}
@@ -122,7 +122,7 @@ public function move(Entry $entry, string $newParent)
$rdn = $this->parseRdnFromEntry($entry);
// deleteOldRdn does not matter here, since the Rdn will not be changing in the move.
if (!@ldap_rename($con, $entry->getDn(), $rdn, $newParent, true)) {
- throw new LdapException(sprintf('Could not move entry "%s" to "%s": %s.', $entry->getDn(), $newParent, ldap_error($con)));
+ throw new LdapException(sprintf('Could not move entry "%s" to "%s": '.ldap_error($con), $entry->getDn(), $newParent));
}
}
diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php
index 533d0e617618b..af6edfcfcc9fc 100644
--- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php
+++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php
@@ -49,7 +49,7 @@ public function __destruct()
continue;
}
if (!ldap_free_result($result)) {
- throw new LdapException(sprintf('Could not free results: %s.', ldap_error($con)));
+ throw new LdapException('Could not free results: '.ldap_error($con));
}
}
$this->results = null;
diff --git a/src/Symfony/Component/Lock/composer.json b/src/Symfony/Component/Lock/composer.json
index 8e82285b40f1e..d604d2bd6837d 100644
--- a/src/Symfony/Component/Lock/composer.json
+++ b/src/Symfony/Component/Lock/composer.json
@@ -21,7 +21,6 @@
},
"require-dev": {
"doctrine/dbal": "~2.5",
- "mongodb/mongodb": "~1.1",
"predis/predis": "~1.0"
},
"conflict": {
diff --git a/src/Symfony/Component/Lock/phpunit.xml.dist b/src/Symfony/Component/Lock/phpunit.xml.dist
index 96c3ea1903abe..4a066573f7d08 100644
--- a/src/Symfony/Component/Lock/phpunit.xml.dist
+++ b/src/Symfony/Component/Lock/phpunit.xml.dist
@@ -12,7 +12,6 @@
-
diff --git a/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesApiTransport.php
index c9cd0ad8cc6f6..95cbdbfd987b7 100644
--- a/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesApiTransport.php
+++ b/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesApiTransport.php
@@ -65,7 +65,7 @@ protected function doSendApi(SentMessage $sentMessage, Email $email, Envelope $e
$result = new \SimpleXMLElement($response->getContent(false));
if (200 !== $response->getStatusCode()) {
- throw new HttpTransportException(sprintf('Unable to send an email: %s (code %d).', $result->Error->Message, $result->Error->Code), $response);
+ throw new HttpTransportException(sprintf('Unable to send an email: '.$result->Error->Message.' (code %d).', $result->Error->Code), $response);
}
$property = $payload['Action'].'Result';
diff --git a/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesHttpTransport.php b/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesHttpTransport.php
index 9cedbdf4c7f7c..f11e970f23cf5 100644
--- a/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesHttpTransport.php
+++ b/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesHttpTransport.php
@@ -65,7 +65,7 @@ protected function doSendHttp(SentMessage $message): ResponseInterface
$result = new \SimpleXMLElement($response->getContent(false));
if (200 !== $response->getStatusCode()) {
- throw new HttpTransportException(sprintf('Unable to send an email: %s (code %d).', $result->Error->Message, $result->Error->Code), $response);
+ throw new HttpTransportException(sprintf('Unable to send an email: '.$result->Error->Message.' (code %d).', $result->Error->Code), $response);
}
$message->setMessageId($result->SendRawEmailResult->MessageId);
diff --git a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillApiTransport.php
index 9b7c181b2943d..6ea7eae4d1eed 100644
--- a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillApiTransport.php
+++ b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillApiTransport.php
@@ -51,7 +51,7 @@ protected function doSendApi(SentMessage $sentMessage, Email $email, Envelope $e
$result = $response->toArray(false);
if (200 !== $response->getStatusCode()) {
if ('error' === ($result['status'] ?? false)) {
- throw new HttpTransportException(sprintf('Unable to send an email: %s (code %d).', $result['message'], $result['code']), $response);
+ throw new HttpTransportException(sprintf('Unable to send an email: '.$result['message'].' (code %d).', $result['code']), $response);
}
throw new HttpTransportException(sprintf('Unable to send an email (code %d).', $result['code']), $response);
diff --git a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillHttpTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillHttpTransport.php
index 92f90b8563fa5..d2058799ec957 100644
--- a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillHttpTransport.php
+++ b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillHttpTransport.php
@@ -58,7 +58,7 @@ protected function doSendHttp(SentMessage $message): ResponseInterface
$result = $response->toArray(false);
if (200 !== $response->getStatusCode()) {
if ('error' === ($result['status'] ?? false)) {
- throw new HttpTransportException(sprintf('Unable to send an email: %s (code %d).', $result['message'], $result['code']), $response);
+ throw new HttpTransportException(sprintf('Unable to send an email: '.$result['message'].' (code %d).', $result['code']), $response);
}
throw new HttpTransportException(sprintf('Unable to send an email (code %d).', $result['code']), $response);
diff --git a/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunApiTransport.php
index c8ed383fcc66b..2b6075c723eed 100644
--- a/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunApiTransport.php
+++ b/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunApiTransport.php
@@ -65,10 +65,10 @@ protected function doSendApi(SentMessage $sentMessage, Email $email, Envelope $e
$result = $response->toArray(false);
if (200 !== $response->getStatusCode()) {
if ('application/json' === $response->getHeaders(false)['content-type'][0]) {
- throw new HttpTransportException(sprintf('Unable to send an email: %s (code %d).', $result['message'], $response->getStatusCode()), $response);
+ throw new HttpTransportException(sprintf('Unable to send an email: '.$result['message'].' (code %d).', $response->getStatusCode()), $response);
}
- throw new HttpTransportException(sprintf('Unable to send an email: %s (code %d).', $response->getContent(false), $response->getStatusCode()), $response);
+ throw new HttpTransportException(sprintf('Unable to send an email: '.$response->getContent(false).' (code %d).', $response->getStatusCode()), $response);
}
$sentMessage->setMessageId($result['id']);
diff --git a/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunHttpTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunHttpTransport.php
index 3ed6f73369fc8..72fbaac1e77e3 100644
--- a/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunHttpTransport.php
+++ b/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunHttpTransport.php
@@ -67,10 +67,10 @@ protected function doSendHttp(SentMessage $message): ResponseInterface
$result = $response->toArray(false);
if (200 !== $response->getStatusCode()) {
if ('application/json' === $response->getHeaders(false)['content-type'][0]) {
- throw new HttpTransportException(sprintf('Unable to send an email: %s (code %d).', $result['message'], $response->getStatusCode()), $response);
+ throw new HttpTransportException(sprintf('Unable to send an email: '.$result['message'].' (code %d).', $response->getStatusCode()), $response);
}
- throw new HttpTransportException(sprintf('Unable to send an email: %s (code %d).', $response->getContent(false), $response->getStatusCode()), $response);
+ throw new HttpTransportException(sprintf('Unable to send an email: '.$response->getContent(false).' (code %d).', $response->getStatusCode()), $response);
}
$message->setMessageId($result['id']);
diff --git a/src/Symfony/Component/Mailer/Bridge/Postmark/Transport/PostmarkApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Postmark/Transport/PostmarkApiTransport.php
index 352b2eded1568..a7c185264097f 100644
--- a/src/Symfony/Component/Mailer/Bridge/Postmark/Transport/PostmarkApiTransport.php
+++ b/src/Symfony/Component/Mailer/Bridge/Postmark/Transport/PostmarkApiTransport.php
@@ -54,7 +54,7 @@ protected function doSendApi(SentMessage $sentMessage, Email $email, Envelope $e
$result = $response->toArray(false);
if (200 !== $response->getStatusCode()) {
- throw new HttpTransportException(sprintf('Unable to send an email: %s (code %d).', $result['Message'], $result['ErrorCode']), $response);
+ throw new HttpTransportException(sprintf('Unable to send an email: '.$result['Message'].' (code %d).', $result['ErrorCode']), $response);
}
$sentMessage->setMessageId($result['MessageID']);
diff --git a/src/Symfony/Component/Mailer/Bridge/Sendgrid/Transport/SendgridApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Sendgrid/Transport/SendgridApiTransport.php
index 550fd4bf331a5..aee333ddb16cf 100644
--- a/src/Symfony/Component/Mailer/Bridge/Sendgrid/Transport/SendgridApiTransport.php
+++ b/src/Symfony/Component/Mailer/Bridge/Sendgrid/Transport/SendgridApiTransport.php
@@ -53,7 +53,7 @@ protected function doSendApi(SentMessage $sentMessage, Email $email, Envelope $e
if (202 !== $response->getStatusCode()) {
$errors = $response->toArray(false);
- throw new HttpTransportException(sprintf('Unable to send an email: %s (code %d).', implode('; ', array_column($errors['errors'], 'message')), $response->getStatusCode()), $response);
+ throw new HttpTransportException(sprintf('Unable to send an email: '.implode('; ', array_column($errors['errors'], 'message')).' (code %d).', $response->getStatusCode()), $response);
}
$sentMessage->setMessageId($response->getHeaders(false)['x-message-id'][0]);
diff --git a/src/Symfony/Component/Mailer/EventListener/EnvelopeListener.php b/src/Symfony/Component/Mailer/EventListener/EnvelopeListener.php
index cbb3922a19a46..570a9e39551f5 100644
--- a/src/Symfony/Component/Mailer/EventListener/EnvelopeListener.php
+++ b/src/Symfony/Component/Mailer/EventListener/EnvelopeListener.php
@@ -14,6 +14,7 @@
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Mailer\Event\MessageEvent;
use Symfony\Component\Mime\Address;
+use Symfony\Component\Mime\Message;
/**
* Manipulates the Envelope of a Message.
@@ -43,6 +44,13 @@ public function onMessage(MessageEvent $event): void
{
if ($this->sender) {
$event->getEnvelope()->setSender($this->sender);
+
+ $message = $event->getMessage();
+ if ($message instanceof Message) {
+ if (!$message->getHeaders()->has('Sender') && !$message->getHeaders()->has('From')) {
+ $message->getHeaders()->addMailboxHeader('Sender', $this->sender->getAddress());
+ }
+ }
}
if ($this->recipients) {
diff --git a/src/Symfony/Component/Mailer/Tests/Transport/Smtp/SmtpTransportTest.php b/src/Symfony/Component/Mailer/Tests/Transport/Smtp/SmtpTransportTest.php
index 7ec8313612049..2c8d91e9e4402 100644
--- a/src/Symfony/Component/Mailer/Tests/Transport/Smtp/SmtpTransportTest.php
+++ b/src/Symfony/Component/Mailer/Tests/Transport/Smtp/SmtpTransportTest.php
@@ -13,6 +13,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mailer\Envelope;
+use Symfony\Component\Mailer\Exception\TransportException;
use Symfony\Component\Mailer\Transport\Smtp\SmtpTransport;
use Symfony\Component\Mailer\Transport\Smtp\Stream\AbstractStream;
use Symfony\Component\Mailer\Transport\Smtp\Stream\SocketStream;
@@ -43,6 +44,29 @@ public function testSendDoesNotPingBelowThreshold(): void
$this->assertNotContains("NOOP\r\n", $stream->getCommands());
}
+ public function testSendPingAfterTransportException(): void
+ {
+ $stream = new DummyStream();
+ $envelope = new Envelope(new Address('sender@example.org'), [new Address('recipient@example.org')]);
+
+ $transport = new SmtpTransport($stream);
+ $transport->send(new RawMessage('Message 1'), $envelope);
+ $stream->close();
+ $catch = false;
+
+ try {
+ $transport->send(new RawMessage('Message 2'), $envelope);
+ } catch (TransportException $exception) {
+ $catch = true;
+ }
+ $this->assertTrue($catch);
+ $this->assertTrue($stream->isClosed());
+
+ $transport->send(new RawMessage('Message 3'), $envelope);
+
+ $this->assertFalse($stream->isClosed());
+ }
+
public function testSendDoesPingAboveThreshold(): void
{
$stream = new DummyStream();
@@ -76,13 +100,23 @@ class DummyStream extends AbstractStream
*/
private $commands;
+ /**
+ * @var bool
+ */
+ private $closed = true;
+
public function initialize(): void
{
+ $this->closed = false;
$this->nextResponse = '220 localhost';
}
public function write(string $bytes, $debug = true): void
{
+ if ($this->closed) {
+ throw new TransportException('Unable to write bytes on the wire.');
+ }
+
$this->commands[] = $bytes;
if (0 === strpos($bytes, 'DATA')) {
@@ -120,4 +154,14 @@ protected function getReadConnectionDescription(): string
{
return 'null';
}
+
+ public function close(): void
+ {
+ $this->closed = true;
+ }
+
+ public function isClosed(): bool
+ {
+ return $this->closed;
+ }
}
diff --git a/src/Symfony/Component/Mailer/Tests/Transport/Smtp/Stream/SocketStreamTest.php b/src/Symfony/Component/Mailer/Tests/Transport/Smtp/Stream/SocketStreamTest.php
index d7912f9ccba2d..a67a1629d0169 100644
--- a/src/Symfony/Component/Mailer/Tests/Transport/Smtp/Stream/SocketStreamTest.php
+++ b/src/Symfony/Component/Mailer/Tests/Transport/Smtp/Stream/SocketStreamTest.php
@@ -19,7 +19,7 @@ class SocketStreamTest extends TestCase
public function testSocketErrorNoConnection()
{
$this->expectException('Symfony\Component\Mailer\Exception\TransportException');
- $this->expectExceptionMessageRegExp('/Connection refused|unable to connect/');
+ $this->expectExceptionMessageMatches('/Connection refused|unable to connect/');
$s = new SocketStream();
$s->setTimeout(0.1);
$s->setPort(9999);
@@ -29,7 +29,7 @@ public function testSocketErrorNoConnection()
public function testSocketErrorBeforeConnectError()
{
$this->expectException('Symfony\Component\Mailer\Exception\TransportException');
- $this->expectExceptionMessageRegExp('/no valid certs found cafile stream|Unable to find the socket transport "ssl"/');
+ $this->expectExceptionMessageMatches('/no valid certs found cafile stream|Unable to find the socket transport "ssl"/');
$s = new SocketStream();
$s->setStreamOptions([
'ssl' => [
diff --git a/src/Symfony/Component/Mailer/Transport/AbstractApiTransport.php b/src/Symfony/Component/Mailer/Transport/AbstractApiTransport.php
index e1a2d5eee2ac8..37b92a699bbc6 100644
--- a/src/Symfony/Component/Mailer/Transport/AbstractApiTransport.php
+++ b/src/Symfony/Component/Mailer/Transport/AbstractApiTransport.php
@@ -31,7 +31,7 @@ protected function doSendHttp(SentMessage $message): ResponseInterface
try {
$email = MessageConverter::toEmail($message->getOriginalMessage());
} catch (\Exception $e) {
- throw new RuntimeException(sprintf('Unable to send message with the "%s" transport: %s.', __CLASS__, $e->getMessage()), 0, $e);
+ throw new RuntimeException(sprintf('Unable to send message with the "%s" transport: '.$e->getMessage(), __CLASS__), 0, $e);
}
return $this->doSendApi($message, $email, $message->getEnvelope());
diff --git a/src/Symfony/Component/Mailer/Transport/AbstractTransport.php b/src/Symfony/Component/Mailer/Transport/AbstractTransport.php
index 1bc3fa12a5616..33f75fb3faa4f 100644
--- a/src/Symfony/Component/Mailer/Transport/AbstractTransport.php
+++ b/src/Symfony/Component/Mailer/Transport/AbstractTransport.php
@@ -63,10 +63,6 @@ public function send(RawMessage $message, Envelope $envelope = null): ?SentMessa
$envelope = $event->getEnvelope();
}
- if (!$envelope->getRecipients()) {
- return null;
- }
-
$message = new SentMessage($message, $envelope);
$this->doSend($message);
diff --git a/src/Symfony/Component/Mailer/Transport/Smtp/EsmtpTransport.php b/src/Symfony/Component/Mailer/Transport/Smtp/EsmtpTransport.php
index 3f68227ba4190..afa522ae97e13 100644
--- a/src/Symfony/Component/Mailer/Transport/Smtp/EsmtpTransport.php
+++ b/src/Symfony/Component/Mailer/Transport/Smtp/EsmtpTransport.php
@@ -106,6 +106,9 @@ protected function doHeloCommand(): void
/** @var SocketStream $stream */
$stream = $this->getStream();
+ // WARNING: !$stream->isTLS() is right, 100% sure :)
+ // if you think that the ! should be removed, read the code again
+ // if doing so "fixes" your issue then it probably means your SMTP server behaves incorrectly or is wrongly configured
if (!$stream->isTLS() && \defined('OPENSSL_VERSION_NUMBER') && \array_key_exists('STARTTLS', $capabilities)) {
$this->executeCommand("STARTTLS\r\n", [220]);
diff --git a/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php b/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php
index 091b5e2bc5a60..26f2057f941ae 100644
--- a/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php
+++ b/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php
@@ -206,12 +206,11 @@ protected function doSend(SentMessage $message): void
$this->stream->flush();
$this->executeCommand("\r\n.\r\n", [250]);
$message->appendDebug($this->stream->getDebug());
+ $this->lastMessageTime = microtime(true);
} catch (TransportExceptionInterface $e) {
$e->appendDebug($this->stream->getDebug());
-
+ $this->lastMessageTime = 0;
throw $e;
- } finally {
- $this->lastMessageTime = microtime(true);
}
}
diff --git a/src/Symfony/Component/Mailer/Transport/Smtp/Stream/ProcessStream.php b/src/Symfony/Component/Mailer/Transport/Smtp/Stream/ProcessStream.php
index 455f739a15faa..a8a8603807d27 100644
--- a/src/Symfony/Component/Mailer/Transport/Smtp/Stream/ProcessStream.php
+++ b/src/Symfony/Component/Mailer/Transport/Smtp/Stream/ProcessStream.php
@@ -41,7 +41,7 @@ public function initialize(): void
$this->stream = proc_open($this->command, $descriptorSpec, $pipes);
stream_set_blocking($pipes[2], false);
if ($err = stream_get_contents($pipes[2])) {
- throw new TransportException(sprintf('Process could not be started: %s.', $err));
+ throw new TransportException('Process could not be started: '.$err);
}
$this->in = &$pipes[0];
$this->out = &$pipes[1];
diff --git a/src/Symfony/Component/Mailer/Transport/Smtp/Stream/SocketStream.php b/src/Symfony/Component/Mailer/Transport/Smtp/Stream/SocketStream.php
index debeeb4b01cb9..5aa86296cbc03 100644
--- a/src/Symfony/Component/Mailer/Transport/Smtp/Stream/SocketStream.php
+++ b/src/Symfony/Component/Mailer/Transport/Smtp/Stream/SocketStream.php
@@ -135,7 +135,7 @@ public function initialize(): void
$streamContext = stream_context_create($options);
set_error_handler(function ($type, $msg) {
- throw new TransportException(sprintf('Connection could not be established with host "%s": %s.', $this->url, $msg));
+ throw new TransportException(sprintf('Connection could not be established with host "%s": '.$msg, $this->url));
});
try {
$this->stream = stream_socket_client($this->url, $errno, $errstr, $this->timeout, STREAM_CLIENT_CONNECT, $streamContext);
diff --git a/src/Symfony/Component/Messenger/Tests/Transport/Receiver/SingleMessageReceiverTest.php b/src/Symfony/Component/Messenger/Tests/Transport/Receiver/SingleMessageReceiverTest.php
index e8f5ef4ce1720..bfebfc5aa3f02 100644
--- a/src/Symfony/Component/Messenger/Tests/Transport/Receiver/SingleMessageReceiverTest.php
+++ b/src/Symfony/Component/Messenger/Tests/Transport/Receiver/SingleMessageReceiverTest.php
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
-namespace Symfony\Component\Messenger\Tests\Transport\Sender;
+namespace Symfony\Component\Messenger\Tests\Transport\Receiver;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Messenger\Envelope;
diff --git a/src/Symfony/Component/Messenger/Tests/Transport/RedisExt/ConnectionTest.php b/src/Symfony/Component/Messenger/Tests/Transport/RedisExt/ConnectionTest.php
index 0be034dd3de3d..e278cfb0ddf0e 100644
--- a/src/Symfony/Component/Messenger/Tests/Transport/RedisExt/ConnectionTest.php
+++ b/src/Symfony/Component/Messenger/Tests/Transport/RedisExt/ConnectionTest.php
@@ -62,6 +62,14 @@ public function testFromDsnWithOptions()
);
}
+ public function testFromDsnWithOptionsAndTrailingSlash()
+ {
+ $this->assertEquals(
+ Connection::fromDsn('redis://localhost/', ['stream' => 'queue', 'group' => 'group1', 'consumer' => 'consumer1', 'auto_setup' => false, 'serializer' => 2]),
+ Connection::fromDsn('redis://localhost/queue/group1/consumer1?serializer=2&auto_setup=0')
+ );
+ }
+
public function testFromDsnWithQueryOptions()
{
$this->assertEquals(
diff --git a/src/Symfony/Component/Messenger/Tests/Transport/Serialization/PhpSerializerTest.php b/src/Symfony/Component/Messenger/Tests/Transport/Serialization/PhpSerializerTest.php
index 6439873fe94cc..5076577b023fc 100644
--- a/src/Symfony/Component/Messenger/Tests/Transport/Serialization/PhpSerializerTest.php
+++ b/src/Symfony/Component/Messenger/Tests/Transport/Serialization/PhpSerializerTest.php
@@ -44,7 +44,7 @@ public function testDecodingFailsWithMissingBodyKey()
public function testDecodingFailsWithBadFormat()
{
$this->expectException(MessageDecodingFailedException::class);
- $this->expectExceptionMessageRegExp('/Could not decode/');
+ $this->expectExceptionMessageMatches('/Could not decode/');
$serializer = new PhpSerializer();
@@ -56,7 +56,7 @@ public function testDecodingFailsWithBadFormat()
public function testDecodingFailsWithBadClass()
{
$this->expectException(MessageDecodingFailedException::class);
- $this->expectExceptionMessageRegExp('/class "ReceivedSt0mp" not found/');
+ $this->expectExceptionMessageMatches('/class "ReceivedSt0mp" not found/');
$serializer = new PhpSerializer();
diff --git a/src/Symfony/Component/Messenger/Tests/Transport/Sync/SyncTransportFactoryTest.php b/src/Symfony/Component/Messenger/Tests/Transport/Sync/SyncTransportFactoryTest.php
index 021c7ae9706a4..63aa43fb7ca0f 100644
--- a/src/Symfony/Component/Messenger/Tests/Transport/Sync/SyncTransportFactoryTest.php
+++ b/src/Symfony/Component/Messenger/Tests/Transport/Sync/SyncTransportFactoryTest.php
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
-namespace Symfony\Component\Messenger\Tests\Transport\AmqpExt;
+namespace Symfony\Component\Messenger\Tests\Transport\Sync;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Messenger\MessageBusInterface;
diff --git a/src/Symfony/Component/Messenger/Tests/Transport/Sync/SyncTransportTest.php b/src/Symfony/Component/Messenger/Tests/Transport/Sync/SyncTransportTest.php
index 13549e27582a1..8ea4eb2e00522 100644
--- a/src/Symfony/Component/Messenger/Tests/Transport/Sync/SyncTransportTest.php
+++ b/src/Symfony/Component/Messenger/Tests/Transport/Sync/SyncTransportTest.php
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
-namespace Symfony\Component\Messenger\Tests\Transport\AmqpExt;
+namespace Symfony\Component\Messenger\Tests\Transport\Sync;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Messenger\Envelope;
diff --git a/src/Symfony/Component/Messenger/Transport/RedisExt/Connection.php b/src/Symfony/Component/Messenger/Transport/RedisExt/Connection.php
index 504fe0a10fa98..bb818512c2fda 100644
--- a/src/Symfony/Component/Messenger/Transport/RedisExt/Connection.php
+++ b/src/Symfony/Component/Messenger/Transport/RedisExt/Connection.php
@@ -56,11 +56,17 @@ public function __construct(array $configuration, array $connectionCredentials =
$this->connection->setOption(\Redis::OPT_SERIALIZER, $redisOptions['serializer'] ?? \Redis::SERIALIZER_PHP);
if (isset($connectionCredentials['auth']) && !$this->connection->auth($connectionCredentials['auth'])) {
- throw new InvalidArgumentException(sprintf('Redis connection failed: %s.', $redis->getLastError()));
+ throw new InvalidArgumentException('Redis connection failed: '.$redis->getLastError());
}
if (($dbIndex = $configuration['dbindex'] ?? self::DEFAULT_OPTIONS['dbindex']) && !$this->connection->select($dbIndex)) {
- throw new InvalidArgumentException(sprintf('Redis connection failed: %s.', $redis->getLastError()));
+ throw new InvalidArgumentException('Redis connection failed: '.$redis->getLastError());
+ }
+
+ foreach (['stream', 'group', 'consumer'] as $key) {
+ if (isset($configuration[$key]) && '' === $configuration[$key]) {
+ throw new InvalidArgumentException(sprintf('"%s" should be configured, got an empty string.', $key));
+ }
}
$this->stream = $configuration['stream'] ?? self::DEFAULT_OPTIONS['stream'];
@@ -77,7 +83,7 @@ public static function fromDsn(string $dsn, array $redisOptions = [], \Redis $re
throw new InvalidArgumentException(sprintf('The given Redis DSN "%s" is invalid.', $dsn));
}
- $pathParts = explode('/', $parsedUrl['path'] ?? '');
+ $pathParts = explode('/', rtrim($parsedUrl['path'] ?? '', '/'));
$stream = $pathParts[1] ?? $redisOptions['stream'] ?? null;
$group = $pathParts[2] ?? $redisOptions['group'] ?? null;
diff --git a/src/Symfony/Component/Messenger/Transport/Serialization/Serializer.php b/src/Symfony/Component/Messenger/Transport/Serialization/Serializer.php
index 8d466500fd9a6..22d48f7e23012 100644
--- a/src/Symfony/Component/Messenger/Transport/Serialization/Serializer.php
+++ b/src/Symfony/Component/Messenger/Transport/Serialization/Serializer.php
@@ -80,7 +80,7 @@ public function decode(array $encodedEnvelope): Envelope
try {
$message = $this->serializer->deserialize($encodedEnvelope['body'], $encodedEnvelope['headers']['type'], $this->format, $context);
} catch (ExceptionInterface $e) {
- throw new MessageDecodingFailedException(sprintf('Could not decode message: %s.', $e->getMessage()), $e->getCode(), $e);
+ throw new MessageDecodingFailedException('Could not decode message: '.$e->getMessage(), $e->getCode(), $e);
}
return new Envelope($message, $stamps);
@@ -118,7 +118,7 @@ private function decodeStamps(array $encodedEnvelope): array
try {
$stamps[] = $this->serializer->deserialize($value, substr($name, \strlen(self::STAMP_HEADER_PREFIX)).'[]', $this->format, $this->context);
} catch (ExceptionInterface $e) {
- throw new MessageDecodingFailedException(sprintf('Could not decode stamp: %s.', $e->getMessage()), $e->getCode(), $e);
+ throw new MessageDecodingFailedException('Could not decode stamp: '.$e->getMessage(), $e->getCode(), $e);
}
}
if ($stamps) {
diff --git a/src/Symfony/Component/Mime/Message.php b/src/Symfony/Component/Mime/Message.php
index 5b4e67f1dbc5b..94a49764e4912 100644
--- a/src/Symfony/Component/Mime/Message.php
+++ b/src/Symfony/Component/Mime/Message.php
@@ -74,7 +74,10 @@ public function getPreparedHeaders(): Headers
$headers = clone $this->headers;
if (!$headers->has('From')) {
- throw new LogicException('An email must have a "From" header.');
+ if (!$headers->has('Sender')) {
+ throw new LogicException('An email must have a "From" or a "Sender" header.');
+ }
+ $headers->addMailboxListHeader('From', [$headers->get('Sender')->getAddress()]);
}
$headers->addTextHeader('MIME-Version', '1.0');
@@ -119,8 +122,12 @@ public function toIterable(): iterable
public function ensureValidity()
{
- if (!$this->headers->has('From')) {
- throw new LogicException('An email must have a "From" header.');
+ if (!$this->headers->has('To')) {
+ throw new LogicException('An email must have a "To" header.');
+ }
+
+ if (!$this->headers->has('From') && !$this->headers->has('Sender')) {
+ throw new LogicException('An email must have a "From" or a "Sender" header.');
}
parent::ensureValidity();
@@ -133,7 +140,7 @@ public function generateMessageId(): string
} elseif ($this->headers->has('From')) {
$sender = $this->headers->get('From')->getAddresses()[0];
} else {
- throw new LogicException('An email must have a "From" or a "Sender" header to compute a Messsage ID.');
+ throw new LogicException('An email must have a "From" or a "Sender" header.');
}
return bin2hex(random_bytes(16)).strstr($sender->getAddress(), '@');
diff --git a/src/Symfony/Component/Mime/Tests/Crypto/SMimeSignerTest.php b/src/Symfony/Component/Mime/Tests/Crypto/SMimeSignerTest.php
index 0a86c3c90e1e7..5522bc6f55cd7 100644
--- a/src/Symfony/Component/Mime/Tests/Crypto/SMimeSignerTest.php
+++ b/src/Symfony/Component/Mime/Tests/Crypto/SMimeSignerTest.php
@@ -99,6 +99,7 @@ public function testSignedMessageWithBcc()
{
$message = (new Email())
->date(new \DateTime('2019-04-07 10:36:30', new \DateTimeZone('Europe/Paris')))
+ ->to('fabien@symfony.com')
->addBcc('fabien@symfony.com', 's.stok@rollerscapes.net')
->subject('I am your sign of fear')
->from('noreply@example.com')
@@ -115,8 +116,9 @@ public function testSignedMessageWithAttachments()
$message = new Email((new Headers())
->addDateHeader('Date', new \DateTime('2019-04-07 10:36:30', new \DateTimeZone('Europe/Paris')))
->addMailboxListHeader('From', ['fabien@symfony.com'])
+ ->addMailboxListHeader('To', ['fabien@symfony.com'])
);
- $message->html($content = 'html content ');
+ $message->html('html content ');
$message->text('text content');
$message->attach(fopen(__DIR__.'/../Fixtures/mimetypes/test', 'r'));
$message->attach(fopen(__DIR__.'/../Fixtures/mimetypes/test.gif', 'r'), 'test.gif');
diff --git a/src/Symfony/Component/Mime/Tests/EmailTest.php b/src/Symfony/Component/Mime/Tests/EmailTest.php
index bfd30053af09e..230df0791e15b 100644
--- a/src/Symfony/Component/Mime/Tests/EmailTest.php
+++ b/src/Symfony/Component/Mime/Tests/EmailTest.php
@@ -251,62 +251,62 @@ public function testGenerateBody()
$att = new DataPart($file = fopen(__DIR__.'/Fixtures/mimetypes/test', 'r'));
$img = new DataPart($image = fopen(__DIR__.'/Fixtures/mimetypes/test.gif', 'r'), 'test.gif');
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->text('text content');
$this->assertEquals($text, $e->getBody());
$this->assertEquals('text content', $e->getTextBody());
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->html('html content');
$this->assertEquals($html, $e->getBody());
$this->assertEquals('html content', $e->getHtmlBody());
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->html('html content');
$e->text('text content');
$this->assertEquals(new AlternativePart($text, $html), $e->getBody());
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->html('html content', 'iso-8859-1');
$e->text('text content', 'iso-8859-1');
$this->assertEquals('iso-8859-1', $e->getTextCharset());
$this->assertEquals('iso-8859-1', $e->getHtmlCharset());
$this->assertEquals(new AlternativePart(new TextPart('text content', 'iso-8859-1'), new TextPart('html content', 'iso-8859-1', 'html')), $e->getBody());
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->attach($file);
$e->text('text content');
$this->assertEquals(new MixedPart($text, $att), $e->getBody());
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->attach($file);
$e->html('html content');
$this->assertEquals(new MixedPart($html, $att), $e->getBody());
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->attach($file);
$this->assertEquals(new MixedPart($att), $e->getBody());
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->html('html content');
$e->text('text content');
$e->attach($file);
$this->assertEquals(new MixedPart(new AlternativePart($text, $html), $att), $e->getBody());
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->html('html content');
$e->text('text content');
$e->attach($file);
$e->attach($image, 'test.gif');
$this->assertEquals(new MixedPart(new AlternativePart($text, $html), $att, $img), $e->getBody());
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->text('text content');
$e->attach($file);
$e->attach($image, 'test.gif');
$this->assertEquals(new MixedPart($text, $att, $img), $e->getBody());
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->html($content = 'html content ');
$e->text('text content');
$e->attach($file);
@@ -314,7 +314,7 @@ public function testGenerateBody()
$fullhtml = new TextPart($content, 'utf-8', 'html');
$this->assertEquals(new MixedPart(new AlternativePart($text, $fullhtml), $att, $img), $e->getBody());
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->html($content = 'html content ');
$e->text('text content');
$e->attach($file);
@@ -334,7 +334,7 @@ public function testGenerateBody()
fwrite($r, $content);
rewind($r);
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->html($r);
// embedding the same image twice results in one image only in the email
$e->embed($image, 'test.gif');
@@ -373,6 +373,7 @@ public function testSerialize()
$e = new Email();
$e->from('fabien@symfony.com');
+ $e->to('you@example.com');
$e->text($r);
$e->html($r);
$name = __DIR__.'/Fixtures/mimetypes/test';
diff --git a/src/Symfony/Component/Mime/Tests/MessageConverterTest.php b/src/Symfony/Component/Mime/Tests/MessageConverterTest.php
index 6a78086246377..a0e71a08a9416 100644
--- a/src/Symfony/Component/Mime/Tests/MessageConverterTest.php
+++ b/src/Symfony/Component/Mime/Tests/MessageConverterTest.php
@@ -21,7 +21,7 @@ class MessageConverterTest extends TestCase
public function testToEmail()
{
$file = file_get_contents(__DIR__.'/Fixtures/mimetypes/test.gif');
- $email = (new Email())->from('fabien@symfony.com');
+ $email = (new Email())->from('fabien@symfony.com')->to('you@example.com');
$this->assertSame($email, MessageConverter::toEmail($email));
$this->assertConversion((clone $email)->text('text content'));
diff --git a/src/Symfony/Component/Mime/Tests/Part/MessagePartTest.php b/src/Symfony/Component/Mime/Tests/Part/MessagePartTest.php
index 21a4eb03b1292..2713d5bc079c7 100644
--- a/src/Symfony/Component/Mime/Tests/Part/MessagePartTest.php
+++ b/src/Symfony/Component/Mime/Tests/Part/MessagePartTest.php
@@ -22,7 +22,7 @@ class MessagePartTest extends TestCase
{
public function testConstructor()
{
- $p = new MessagePart((new Email())->from('fabien@symfony.com')->text('content'));
+ $p = new MessagePart((new Email())->from('fabien@symfony.com')->to('you@example.com')->text('content'));
$this->assertStringContainsString('content', $p->getBody());
$this->assertStringContainsString('content', $p->bodyToString());
$this->assertStringContainsString('content', implode('', iterator_to_array($p->bodyToIterable())));
diff --git a/src/Symfony/Component/OptionsResolver/Tests/Debug/OptionsResolverIntrospectorTest.php b/src/Symfony/Component/OptionsResolver/Tests/Debug/OptionsResolverIntrospectorTest.php
index d58b120457b49..433d9a8a269d6 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/UnixPipes.php b/src/Symfony/Component/Process/Pipes/UnixPipes.php
index 603d726b47fc9..70fdd29574ec7 100644
--- a/src/Symfony/Component/Process/Pipes/UnixPipes.php
+++ b/src/Symfony/Component/Process/Pipes/UnixPipes.php
@@ -118,7 +118,7 @@ public function readAndWrite(bool $blocking, bool $close = false): array
$read[$type = array_search($pipe, $this->pipes, true)] = '';
do {
- $data = fread($pipe, self::CHUNK_SIZE);
+ $data = @fread($pipe, self::CHUNK_SIZE);
$read[$type] .= $data;
} while (isset($data[0]) && ($close || isset($data[self::CHUNK_SIZE - 1])));
diff --git a/src/Symfony/Component/Process/Pipes/WindowsPipes.php b/src/Symfony/Component/Process/Pipes/WindowsPipes.php
index 6d9976960ad1b..c548092c51fff 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, bool $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/Process/Process.php b/src/Symfony/Component/Process/Process.php
index db4c2919dff59..81568a1c03409 100644
--- a/src/Symfony/Component/Process/Process.php
+++ b/src/Symfony/Component/Process/Process.php
@@ -1648,7 +1648,7 @@ private function replacePlaceholders(string $commandline, array $env)
{
return preg_replace_callback('/"\$\{:([_a-zA-Z]++[_a-zA-Z0-9]*+)\}"/', function ($matches) use ($commandline, $env) {
if (!isset($env[$matches[1]]) || false === $env[$matches[1]]) {
- throw new InvalidArgumentException(sprintf('Command line is missing a value for parameter "%s": %s.', $matches[1], $commandline));
+ throw new InvalidArgumentException(sprintf('Command line is missing a value for parameter "%s": '.$commandline, $matches[1]));
}
return $this->escapeArgument($env[$matches[1]]);
diff --git a/src/Symfony/Component/Process/Tests/ProcessTest.php b/src/Symfony/Component/Process/Tests/ProcessTest.php
index ecbf271aeb8cf..d7461d082f8c1 100644
--- a/src/Symfony/Component/Process/Tests/ProcessTest.php
+++ b/src/Symfony/Component/Process/Tests/ProcessTest.php
@@ -50,7 +50,7 @@ protected function tearDown(): void
public function testInvalidCwd()
{
$this->expectException('Symfony\Component\Process\Exception\RuntimeException');
- $this->expectExceptionMessageRegExp('/The provided cwd ".*" does not exist\./');
+ $this->expectExceptionMessageMatches('/The provided cwd ".*" does not exist\./');
try {
// Check that it works fine if the CWD exists
$cmd = new Process(['echo', 'test'], __DIR__);
@@ -1488,7 +1488,7 @@ public function testPreparedCommandWithQuoteInIt()
public function testPreparedCommandWithMissingValue()
{
$this->expectException('Symfony\Component\Process\Exception\InvalidArgumentException');
- $this->expectExceptionMessage('Command line is missing a value for parameter "abc": echo "${:abc}".');
+ $this->expectExceptionMessage('Command line is missing a value for parameter "abc": echo "${:abc}"');
$p = Process::fromShellCommandline('echo "${:abc}"');
$p->run(null, ['bcd' => 'BCD']);
}
@@ -1496,7 +1496,7 @@ public function testPreparedCommandWithMissingValue()
public function testPreparedCommandWithNoValues()
{
$this->expectException('Symfony\Component\Process\Exception\InvalidArgumentException');
- $this->expectExceptionMessage('Command line is missing a value for parameter "abc": echo "${:abc}".');
+ $this->expectExceptionMessage('Command line is missing a value for parameter "abc": echo "${:abc}"');
$p = Process::fromShellCommandline('echo "${:abc}"');
$p->run(null, []);
}
diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php
index 01f948291ad5c..cac049a0a637e 100644
--- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php
+++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php
@@ -385,7 +385,7 @@ private function readProperty(array $zval, string $property, bool $ignoreInvalid
} 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;
@@ -418,7 +418,7 @@ private function readProperty(array $zval, string $property, bool $ignoreInvalid
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/PropertyAccessorCollectionTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php
index 09aebab87b135..94655b9762742 100644
--- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php
+++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php
@@ -149,7 +149,7 @@ public function testSetValueCallsAdderAndRemoverForNestedCollections()
public function testSetValueFailsIfNoAdderNorRemoverFound()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException');
- $this->expectExceptionMessageRegExp('/Could not determine access type for property "axes" in class "Mock_PropertyAccessorCollectionTest_CarNoAdderAndRemover_[^"]*"./');
+ $this->expectExceptionMessageMatches('/Could not determine access type for property "axes" in class "Mock_PropertyAccessorCollectionTest_CarNoAdderAndRemover_[^"]*"./');
$car = $this->getMockBuilder(__CLASS__.'_CarNoAdderAndRemover')->getMock();
$axesBefore = $this->getContainer([1 => 'second', 3 => 'fourth']);
$axesAfter = $this->getContainer([0 => 'first', 1 => 'second', 2 => 'third']);
@@ -188,7 +188,7 @@ public function testIsWritableReturnsFalseIfNoAdderNorRemoverExists()
public function testSetValueFailsIfAdderAndRemoverExistButValueIsNotTraversable()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException');
- $this->expectExceptionMessageRegExp('/Could not determine access type for property "axes" in class "Symfony\\\\Component\\\\PropertyAccess\\\\Tests\\\\PropertyAccessorCollectionTest_Car[^"]*": The property "axes" in class "Symfony\\\\Component\\\\PropertyAccess\\\\Tests\\\\PropertyAccessorCollectionTest_Car[^"]*" can be defined with the methods "addAxis\(\)", "removeAxis\(\)" but the new value must be an array or an instance of \\\\Traversable, "string" given./');
+ $this->expectExceptionMessageMatches('/Could not determine access type for property "axes" in class "Symfony\\\\Component\\\\PropertyAccess\\\\Tests\\\\PropertyAccessorCollectionTest_Car[^"]*": The property "axes" in class "Symfony\\\\Component\\\\PropertyAccess\\\\Tests\\\\PropertyAccessorCollectionTest_Car[^"]*" can be defined with the methods "addAxis\(\)", "removeAxis\(\)" but the new value must be an array or an instance of \\\\Traversable, "string" given./');
$car = new PropertyAccessorCollectionTest_Car();
$this->propertyAccessor->setValue($car, 'axes', 'Not an array or Traversable');
diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php
index 5304a807f1b75..eeabf7e9f8de1 100644
--- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php
+++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php
@@ -139,7 +139,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');
}
@@ -150,7 +150,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');
}
@@ -768,7 +768,7 @@ public function testAdderAndRemoverArePreferredOverSetterForSameSingularAndPlura
public function testAdderWithoutRemover()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException');
- $this->expectExceptionMessageRegExp('/.*The add method "addFoo" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestAdderRemoverInvalidMethods" was found, but the corresponding remove method "removeFoo" was not found\./');
+ $this->expectExceptionMessageMatches('/.*The add method "addFoo" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestAdderRemoverInvalidMethods" was found, but the corresponding remove method "removeFoo" was not found\./');
$object = new TestAdderRemoverInvalidMethods();
$this->propertyAccessor->setValue($object, 'foos', [1, 2]);
}
@@ -776,7 +776,7 @@ public function testAdderWithoutRemover()
public function testRemoverWithoutAdder()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException');
- $this->expectExceptionMessageRegExp('/.*The remove method "removeBar" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestAdderRemoverInvalidMethods" was found, but the corresponding add method "addBar" was not found\./');
+ $this->expectExceptionMessageMatches('/.*The remove method "removeBar" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestAdderRemoverInvalidMethods" was found, but the corresponding add method "addBar" was not found\./');
$object = new TestAdderRemoverInvalidMethods();
$this->propertyAccessor->setValue($object, 'bars', [1, 2]);
}
@@ -784,7 +784,7 @@ public function testRemoverWithoutAdder()
public function testAdderAndRemoveNeedsTheExactParametersDefined()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException');
- $this->expectExceptionMessageRegExp('/.*The method "addFoo" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestAdderRemoverInvalidArgumentLength" requires 0 arguments, but should accept only 1\. The method "removeFoo" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestAdderRemoverInvalidArgumentLength" requires 2 arguments, but should accept only 1\./');
+ $this->expectExceptionMessageMatches('/.*The method "addFoo" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestAdderRemoverInvalidArgumentLength" requires 0 arguments, but should accept only 1\. The method "removeFoo" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestAdderRemoverInvalidArgumentLength" requires 2 arguments, but should accept only 1\./');
$object = new TestAdderRemoverInvalidArgumentLength();
$this->propertyAccessor->setValue($object, 'foo', [1, 2]);
}
@@ -792,7 +792,7 @@ public function testAdderAndRemoveNeedsTheExactParametersDefined()
public function testSetterNeedsTheExactParametersDefined()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException');
- $this->expectExceptionMessageRegExp('/.*The method "setBar" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestAdderRemoverInvalidArgumentLength" requires 2 arguments, but should accept only 1\./');
+ $this->expectExceptionMessageMatches('/.*The method "setBar" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestAdderRemoverInvalidArgumentLength" requires 2 arguments, but should accept only 1\./');
$object = new TestAdderRemoverInvalidArgumentLength();
$this->propertyAccessor->setValue($object, 'bar', [1, 2]);
}
@@ -800,7 +800,7 @@ public function testSetterNeedsTheExactParametersDefined()
public function testSetterNeedsPublicAccess()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException');
- $this->expectExceptionMessageRegExp('/.*The method "setFoo" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestClassSetValue" was found but does not have public access./');
+ $this->expectExceptionMessageMatches('/.*The method "setFoo" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestClassSetValue" was found but does not have public access./');
$object = new TestClassSetValue(0);
$this->propertyAccessor->setValue($object, 'foo', 1);
}
diff --git a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
index 6de6517b726ef..ccb216b624e68 100644
--- a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
+++ b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
@@ -58,6 +58,9 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
private $enableConstructorExtraction;
private $accessFlags;
+ private $arrayMutatorPrefixesFirst;
+ private $arrayMutatorPrefixesLast;
+
/**
* @param string[]|null $mutatorPrefixes
* @param string[]|null $accessorPrefixes
@@ -70,6 +73,9 @@ public function __construct(array $mutatorPrefixes = null, array $accessorPrefix
$this->arrayMutatorPrefixes = null !== $arrayMutatorPrefixes ? $arrayMutatorPrefixes : self::$defaultArrayMutatorPrefixes;
$this->enableConstructorExtraction = $enableConstructorExtraction;
$this->accessFlags = $accessFlags;
+
+ $this->arrayMutatorPrefixesFirst = array_merge($this->arrayMutatorPrefixes, array_diff($this->mutatorPrefixes, $this->arrayMutatorPrefixes));
+ $this->arrayMutatorPrefixesLast = array_reverse($this->arrayMutatorPrefixesFirst);
}
/**
@@ -405,7 +411,9 @@ private function getMutatorMethod(string $class, string $property): ?array
$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 3f48a1d828e1e..0d3c32206786e 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 phpDocumentor\Reflection\Types\Collection;
use PHPUnit\Framework\TestCase;
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php
index 45fd42c39a641..0fadd46413eec 100644
--- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php
+++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php
@@ -70,6 +70,7 @@ public function testGetProperties()
'realParent',
'xTotals',
'YT',
+ 'date',
'c',
'd',
'e',
@@ -109,6 +110,7 @@ public function testGetPropertiesWithCustomPrefixes()
'foo4',
'foo5',
'files',
+ 'date',
'c',
'd',
'e',
@@ -173,6 +175,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 ec4b75a099a73..bcec074438948 100644
--- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php
+++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php
@@ -210,4 +210,12 @@ public function getXTotals()
public function getYT()
{
}
+
+ public function setDate(\DateTime $date)
+ {
+ }
+
+ public function addDate(\DateTime $date)
+ {
+ }
}
diff --git a/src/Symfony/Component/Routing/Annotation/Route.php b/src/Symfony/Component/Routing/Annotation/Route.php
index 8183b6fc55e97..52b208c41b067 100644
--- a/src/Symfony/Component/Routing/Annotation/Route.php
+++ b/src/Symfony/Component/Routing/Annotation/Route.php
@@ -31,9 +31,6 @@ class Route
private $methods = [];
private $schemes = [];
private $condition;
- private $locale;
- private $format;
- private $utf8;
/**
* @param array $data An array of key/value parameters
diff --git a/src/Symfony/Component/Routing/Loader/Configurator/ImportConfigurator.php b/src/Symfony/Component/Routing/Loader/Configurator/ImportConfigurator.php
index f11b7957525b1..0059a632a1977 100644
--- a/src/Symfony/Component/Routing/Loader/Configurator/ImportConfigurator.php
+++ b/src/Symfony/Component/Routing/Loader/Configurator/ImportConfigurator.php
@@ -13,6 +13,7 @@
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\RouteCompiler;
/**
* @author Nicolas Grekas
@@ -63,6 +64,7 @@ final public function prefix($prefix, bool $trailingSlashOnRoot = true): self
foreach ($prefix as $locale => $localePrefix) {
$localizedRoute = clone $route;
$localizedRoute->setDefault('_locale', $locale);
+ $localizedRoute->setRequirement('_locale', preg_quote($locale, RouteCompiler::REGEX_DELIMITER));
$localizedRoute->setDefault('_canonical_route', $name);
$localizedRoute->setPath($localePrefix.(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
$this->route->add($name.'.'.$locale, $localizedRoute);
diff --git a/src/Symfony/Component/Routing/Loader/XmlFileLoader.php b/src/Symfony/Component/Routing/Loader/XmlFileLoader.php
index da8f34b293c19..31c614950e41a 100644
--- a/src/Symfony/Component/Routing/Loader/XmlFileLoader.php
+++ b/src/Symfony/Component/Routing/Loader/XmlFileLoader.php
@@ -211,6 +211,7 @@ protected function parseImport(RouteCollection $collection, \DOMElement $node, $
$localizedRoute = clone $route;
$localizedRoute->setPath($localePrefix.(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
$localizedRoute->setDefault('_locale', $locale);
+ $localizedRoute->setRequirement('_locale', preg_quote($locale, RouteCompiler::REGEX_DELIMITER));
$localizedRoute->setDefault('_canonical_route', $name);
$subCollection->add($name.'.'.$locale, $localizedRoute);
}
@@ -314,9 +315,9 @@ private function parseConfigs(\DOMElement $node, string $path): array
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/Routing/Loader/YamlFileLoader.php b/src/Symfony/Component/Routing/Loader/YamlFileLoader.php
index 868da9bd30b4b..8d4b9abdb10fa 100644
--- a/src/Symfony/Component/Routing/Loader/YamlFileLoader.php
+++ b/src/Symfony/Component/Routing/Loader/YamlFileLoader.php
@@ -216,6 +216,7 @@ protected function parseImport(RouteCollection $collection, array $config, $path
foreach ($prefix as $locale => $localePrefix) {
$localizedRoute = clone $route;
$localizedRoute->setDefault('_locale', $locale);
+ $localizedRoute->setRequirement('_locale', preg_quote($locale, RouteCompiler::REGEX_DELIMITER));
$localizedRoute->setDefault('_canonical_route', $name);
$localizedRoute->setPath($localePrefix.(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
$subCollection->add($name.'.'.$locale, $localizedRoute);
diff --git a/src/Symfony/Component/Routing/README.md b/src/Symfony/Component/Routing/README.md
index a16d9d7fcbbbb..03b258ec8203b 100644
--- a/src/Symfony/Component/Routing/README.md
+++ b/src/Symfony/Component/Routing/README.md
@@ -3,10 +3,48 @@ Routing Component
The Routing component maps an HTTP request to a set of configuration variables.
+Getting Started
+---------------
+
+```
+$ composer require symfony/routing
+```
+
+```php
+use App\Controller\BlogController;
+use Symfony\Component\Routing\Generator\UrlGenerator;
+use Symfony\Component\Routing\Matcher\UrlMatcher;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+$route = new Route('/blog/{slug}', ['_controller' => BlogController::class]);
+$routes = new RouteCollection();
+$routes->add('blog_show', $route);
+
+$context = new RequestContext();
+
+// Routing can match routes with incoming requests
+$matcher = new UrlMatcher($routes, $context);
+$parameters = $matcher->match('/blog/lorem-ipsum');
+// $parameters = [
+// '_controller' => 'App\Controller\BlogController',
+// 'slug' => 'lorem-ipsum',
+// '_route' => 'blog_show'
+// ]
+
+// Routing can also generate URLs for a given route
+$generator = new UrlGenerator($routes, $context);
+$url = $generator->generate('blog_show', [
+ 'slug' => 'my-blog-post',
+]);
+// $url = '/blog/my-blog-post'
+```
+
Resources
---------
- * [Documentation](https://symfony.com/doc/current/components/routing.html)
+ * [Documentation](https://symfony.com/doc/current/routing.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)
diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/php_dsl_sub_i18n.php b/src/Symfony/Component/Routing/Tests/Fixtures/php_dsl_sub_i18n.php
index e79edc869d2de..e6d846dd463cf 100644
--- a/src/Symfony/Component/Routing/Tests/Fixtures/php_dsl_sub_i18n.php
+++ b/src/Symfony/Component/Routing/Tests/Fixtures/php_dsl_sub_i18n.php
@@ -8,4 +8,6 @@
$add('foo', ['fr' => '/foo']);
$add('bar', ['fr' => '/bar']);
+
+ $routes->add('non_localized', '/non-localized');
};
diff --git a/src/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php
index 789848c66021a..b84d5ff3543be 100644
--- a/src/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php
+++ b/src/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php
@@ -234,6 +234,7 @@ public function testRoutingI18nConfigurator()
$expectedCollection->add('baz.en', (new Route('/baz'))->setDefaults(['_locale' => 'en', '_canonical_route' => 'baz'])->setRequirement('_locale', 'en'));
$expectedCollection->add('c_foo.fr', (new Route('/ench/pub/foo'))->setDefaults(['_locale' => 'fr', '_canonical_route' => 'c_foo'])->setRequirement('_locale', 'fr'));
$expectedCollection->add('c_bar.fr', (new Route('/ench/pub/bar'))->setDefaults(['_locale' => 'fr', '_canonical_route' => 'c_bar'])->setRequirement('_locale', 'fr'));
+ $expectedCollection->add('non_localized.fr', (new Route('/ench/non-localized'))->setDefaults(['_locale' => 'fr', '_canonical_route' => 'non_localized'])->setRequirement('_locale', 'fr'));
$expectedCollection->addResource(new FileResource(realpath(__DIR__.'/../Fixtures/php_dsl_sub_i18n.php')));
$expectedCollection->addResource(new FileResource(realpath(__DIR__.'/../Fixtures/php_dsl_i18n.php')));
diff --git a/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php
index 66d54fc985c4c..383cda0176952 100644
--- a/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php
+++ b/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php
@@ -201,6 +201,9 @@ public function testLocalizedImportsOfNotLocalizedRoutes()
$this->assertEquals('/le-prefix/suffix', $routeCollection->get('imported.fr')->getPath());
$this->assertEquals('/the-prefix/suffix', $routeCollection->get('imported.en')->getPath());
+
+ $this->assertSame('fr', $routeCollection->get('imported.fr')->getRequirement('_locale'));
+ $this->assertSame('en', $routeCollection->get('imported.en')->getRequirement('_locale'));
}
/**
@@ -439,7 +442,7 @@ public function testLoadRouteWithControllerSetInDefaults()
public function testOverrideControllerInDefaults()
{
$this->expectException('InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/The routing file "[^"]*" must not specify both the "controller" attribute and the defaults key "_controller" for "app_blog"/');
+ $this->expectExceptionMessageMatches('/The routing file "[^"]*" must not specify both the "controller" attribute and the defaults key "_controller" for "app_blog"/');
$loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures/controller']));
$loader->load('override_defaults.xml');
}
@@ -471,7 +474,7 @@ public function provideFilesImportingRoutesWithControllers()
public function testImportWithOverriddenController()
{
$this->expectException('InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/The routing file "[^"]*" must not specify both the "controller" attribute and the defaults key "_controller" for the "import" tag/');
+ $this->expectExceptionMessageMatches('/The routing file "[^"]*" must not specify both the "controller" attribute and the defaults key "_controller" for the "import" tag/');
$loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures/controller']));
$loader->load('import_override_defaults.xml');
}
diff --git a/src/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php
index 52c21c287fcf6..e5571b0b7a6eb 100644
--- a/src/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php
+++ b/src/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php
@@ -144,7 +144,7 @@ public function testLoadRouteWithControllerSetInDefaults()
public function testOverrideControllerInDefaults()
{
$this->expectException('InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/The routing file "[^"]*" must not specify both the "controller" key and the defaults key "_controller" for "app_blog"/');
+ $this->expectExceptionMessageMatches('/The routing file "[^"]*" must not specify both the "controller" key and the defaults key "_controller" for "app_blog"/');
$loader = new YamlFileLoader(new FileLocator([__DIR__.'/../Fixtures/controller']));
$loader->load('override_defaults.yml');
}
@@ -176,7 +176,7 @@ public function provideFilesImportingRoutesWithControllers()
public function testImportWithOverriddenController()
{
$this->expectException('InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/The routing file "[^"]*" must not specify both the "controller" key and the defaults key "_controller" for "_static"/');
+ $this->expectExceptionMessageMatches('/The routing file "[^"]*" must not specify both the "controller" key and the defaults key "_controller" for "_static"/');
$loader = new YamlFileLoader(new FileLocator([__DIR__.'/../Fixtures/controller']));
$loader->load('import_override_defaults.yml');
}
@@ -334,6 +334,9 @@ public function testImportingNonLocalizedRoutesWithLocales()
$this->assertCount(2, $routes);
$this->assertEquals('/nl/imported', $routes->get('imported.nl')->getPath());
$this->assertEquals('/en/imported', $routes->get('imported.en')->getPath());
+
+ $this->assertSame('nl', $routes->get('imported.nl')->getRequirement('_locale'));
+ $this->assertSame('en', $routes->get('imported.en')->getRequirement('_locale'));
}
public function testImportingRoutesWithOfficialLocales()
diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php
index 2c9dc44a17735..a7bd1e087d688 100644
--- a/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php
+++ b/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php
@@ -83,14 +83,13 @@ protected function checkAuthentication(UserInterface $user, UsernamePasswordToke
}
try {
- $username = $this->ldap->escape($username, '', LdapInterface::ESCAPE_DN);
-
if ($this->queryString) {
if ('' !== $this->searchDn && '' !== $this->searchPassword) {
$this->ldap->bind($this->searchDn, $this->searchPassword);
} else {
@trigger_error('Using the "query_string" config without using a "search_dn" and a "search_password" is deprecated since Symfony 4.4 and will throw an exception in Symfony 5.0.', E_USER_DEPRECATED);
}
+ $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()) {
@@ -99,6 +98,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/Authentication/Token/Storage/TokenStorage.php b/src/Symfony/Component/Security/Core/Authentication/Token/Storage/TokenStorage.php
index bf491797aa25d..dd8a5ae00420a 100644
--- a/src/Symfony/Component/Security/Core/Authentication/Token/Storage/TokenStorage.php
+++ b/src/Symfony/Component/Security/Core/Authentication/Token/Storage/TokenStorage.php
@@ -49,6 +49,11 @@ public function setToken(TokenInterface $token = null)
@trigger_error(sprintf('Not implementing the "%s::getRoleNames()" method in "%s" is deprecated since Symfony 4.3.', TokenInterface::class, \get_class($token)), E_USER_DEPRECATED);
}
+ if ($token) {
+ // ensure any initializer is called
+ $this->getToken();
+ }
+
$this->initializer = null;
$this->token = $token;
}
diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/Storage/UsageTrackingTokenStorage.php b/src/Symfony/Component/Security/Core/Authentication/Token/Storage/UsageTrackingTokenStorage.php
index 3ce8913aa4fbb..b90d5ab28b635 100644
--- a/src/Symfony/Component/Security/Core/Authentication/Token/Storage/UsageTrackingTokenStorage.php
+++ b/src/Symfony/Component/Security/Core/Authentication/Token/Storage/UsageTrackingTokenStorage.php
@@ -52,6 +52,11 @@ public function getToken(): ?TokenInterface
public function setToken(TokenInterface $token = null): void
{
$this->storage->setToken($token);
+
+ if ($token && $this->enableUsageTracking) {
+ // increments the internal session usage index
+ $this->sessionLocator->get('session')->getMetadataBag();
+ }
}
public function enableUsageTracking(): void
diff --git a/src/Symfony/Component/Security/Core/Authorization/TraceableAccessDecisionManager.php b/src/Symfony/Component/Security/Core/Authorization/TraceableAccessDecisionManager.php
index 7690b3e2264bc..7a9c4d0cfb23a 100644
--- a/src/Symfony/Component/Security/Core/Authorization/TraceableAccessDecisionManager.php
+++ b/src/Symfony/Component/Security/Core/Authorization/TraceableAccessDecisionManager.php
@@ -47,8 +47,10 @@ public function __construct(AccessDecisionManagerInterface $manager)
/**
* {@inheritdoc}
+ *
+ * @param bool $allowMultipleAttributes Whether to allow passing multiple values to the $attributes array
*/
- public function decide(TokenInterface $token, array $attributes, $object = null): bool
+ public function decide(TokenInterface $token, array $attributes, $object = null/*, bool $allowMultipleAttributes = false*/): bool
{
$currentDecisionLog = [
'attributes' => $attributes,
@@ -58,7 +60,7 @@ public function decide(TokenInterface $token, array $attributes, $object = null)
$this->currentLog[] = &$currentDecisionLog;
- $result = $this->manager->decide($token, $attributes, $object);
+ $result = $this->manager->decide($token, $attributes, $object, 3 < \func_num_args() && func_get_arg(3));
$currentDecisionLog['result'] = $result;
diff --git a/src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php b/src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php
index cad50180d181e..a6dbf71a1d1f3 100644
--- a/src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php
+++ b/src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php
@@ -72,10 +72,10 @@ private function createEncoder(array $config, bool $isExtra = false): PasswordEn
$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));
}
$encoder = new $config['class'](...$config['arguments']);
diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php
index 893c8909719fa..fcc950dbe622b 100644
--- a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php
+++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php
@@ -111,16 +111,16 @@ public function testQueryForDn()
;
$ldap = $this->getMockBuilder(LdapInterface::class)->getMock();
+ $ldap
+ ->expects($this->at(0))
+ ->method('bind')
+ ->with('elsa', 'test1234A$');
$ldap
->expects($this->once())
->method('escape')
->with('foo', '')
->willReturn('foo')
;
- $ldap
- ->expects($this->at(1))
- ->method('bind')
- ->with('elsa', 'test1234A$');
$ldap
->expects($this->once())
->method('query')
@@ -151,16 +151,16 @@ public function testQueryWithUserForDn()
;
$ldap = $this->getMockBuilder(LdapInterface::class)->getMock();
+ $ldap
+ ->expects($this->at(0))
+ ->method('bind')
+ ->with('elsa', 'test1234A$');
$ldap
->expects($this->once())
->method('escape')
->with('foo', '')
->willReturn('foo')
;
- $ldap
- ->expects($this->at(1))
- ->method('bind')
- ->with('elsa', 'test1234A$');
$ldap
->expects($this->once())
->method('query')
@@ -195,7 +195,7 @@ public function testEmptyQueryResultShouldThrowAnException()
$ldap = $this->getMockBuilder(LdapInterface::class)->getMock();
$ldap
- ->expects($this->at(1))
+ ->expects($this->at(0))
->method('bind')
->with('elsa', 'test1234A$');
$ldap
diff --git a/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php b/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php
index 816bd097309e8..e00fb12dfb392 100644
--- a/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php
+++ b/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php
@@ -190,7 +190,7 @@ public function testSupportsChecksGuardAuthenticatorsTokenOrigin()
public function testAuthenticateFailsOnNonOriginatingToken()
{
$this->expectException('Symfony\Component\Security\Core\Exception\AuthenticationException');
- $this->expectExceptionMessageRegExp('/second_firewall_0/');
+ $this->expectExceptionMessageMatches('/second_firewall_0/');
$authenticatorA = $this->getMockBuilder(AuthenticatorInterface::class)->getMock();
$authenticators = [$authenticatorA];
diff --git a/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php b/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php
index 21bd1bc1cdb0b..a6718a7232824 100644
--- a/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php
+++ b/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php
@@ -168,7 +168,7 @@ private function attemptSwitchUser(Request $request, string $username): ?TokenIn
try {
$this->provider->loadUserByUsername($nonExistentUsername);
- } catch (AuthenticationException $e) {
+ } catch (\Exception $e) {
}
} catch (AuthenticationException $e) {
$this->provider->loadUserByUsername($currentUsername);
diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php
index 82a5f917d98ee..7e89c0b1b36fb 100644
--- a/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php
+++ b/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php
@@ -411,9 +411,9 @@ protected function runSessionOnKernelResponse($newToken, $original = null)
private function handleEventWithPreviousSession($userProviders, UserInterface $user = null, RememberMeServicesInterface $rememberMeServices = null)
{
- $user = $user ?: new User('foo', 'bar');
+ $tokenUser = $user ?: new User('foo', 'bar');
$session = new Session(new MockArraySessionStorage());
- $session->set('_security_context_key', serialize(new UsernamePasswordToken($user, '', 'context_key', ['ROLE_USER'])));
+ $session->set('_security_context_key', serialize(new UsernamePasswordToken($tokenUser, '', 'context_key', ['ROLE_USER'])));
$request = new Request();
$request->setSession($session);
@@ -442,6 +442,10 @@ private function handleEventWithPreviousSession($userProviders, UserInterface $u
$listener(new RequestEvent($this->getMockBuilder(HttpKernelInterface::class)->getMock(), $request, HttpKernelInterface::MASTER_REQUEST));
if (null !== $usageIndex) {
+ if (null !== $user) {
+ ++$usageIndex;
+ }
+
$this->assertSame($usageIndex, $session->getUsageIndex());
$tokenStorage->getToken();
$this->assertSame(1 + $usageIndex, $session->getUsageIndex());
diff --git a/src/Symfony/Component/Security/Http/composer.json b/src/Symfony/Component/Security/Http/composer.json
index 699ffcf7032b3..6b23f5cc8e311 100644
--- a/src/Symfony/Component/Security/Http/composer.json
+++ b/src/Symfony/Component/Security/Http/composer.json
@@ -17,7 +17,7 @@
],
"require": {
"php": "^7.1.3",
- "symfony/security-core": "^4.4.7",
+ "symfony/security-core": "^4.4.8",
"symfony/http-foundation": "^3.4.40|^4.4.7|^5.0.7",
"symfony/http-kernel": "^4.4",
"symfony/property-access": "^3.4|^4.0|^5.0"
diff --git a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php
index 138f9280dd598..fbfa7574693bf 100644
--- a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php
+++ b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php
@@ -473,7 +473,7 @@ private function buildXml(\DOMNode $parentNode, $data, string $xmlRootNodeName =
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 be4f9c47ae729..4eb8468360435 100644
--- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php
+++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php
@@ -359,7 +359,7 @@ public function denormalize($data, $type, $format = null, array $context = [])
try {
$this->setAttributeValue($object, $attribute, $value, $format, $context);
} catch (InvalidArgumentException $e) {
- throw new NotNormalizableValueException(sprintf('Failed to denormalize attribute "%s" value for class "%s": %s.', $attribute, $type, $e->getMessage()), $e->getCode(), $e);
+ throw new NotNormalizableValueException(sprintf('Failed to denormalize attribute "%s" value for class "%s": '.$e->getMessage(), $attribute, $type), $e->getCode(), $e);
}
}
@@ -482,7 +482,7 @@ private function validateAndDenormalize(string $currentClass, string $attribute,
*/
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);
}
@@ -617,6 +617,7 @@ private function getCacheKey(?string $format, array $context)
unset($context[$key]);
}
unset($context[self::EXCLUDE_FROM_CACHE_KEY]);
+ unset($context[self::OBJECT_TO_POPULATE]);
unset($context['cache_key']); // avoid artificially different keys
try {
diff --git a/src/Symfony/Component/Serializer/Serializer.php b/src/Symfony/Component/Serializer/Serializer.php
index 94770484738f0..3f2461cf96a09 100644
--- a/src/Symfony/Component/Serializer/Serializer.php
+++ b/src/Symfony/Component/Serializer/Serializer.php
@@ -173,7 +173,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 3de0ac086a5eb..01b8950cb4c98 100644
--- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php
@@ -174,6 +174,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();
@@ -448,3 +456,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/Formatter/IntlFormatter.php b/src/Symfony/Component/Translation/Formatter/IntlFormatter.php
index ad4a45b95b5c0..9101a63aa2ee9 100644
--- a/src/Symfony/Component/Translation/Formatter/IntlFormatter.php
+++ b/src/Symfony/Component/Translation/Formatter/IntlFormatter.php
@@ -40,7 +40,7 @@ public function formatIntl(string $message, string $locale, array $parameters =
try {
$this->cache[$locale][$message] = $formatter = new \MessageFormatter($locale, $message);
} catch (\IntlException $e) {
- throw new InvalidArgumentException(sprintf('Invalid message format (error #%d): %s.', intl_get_error_code(), intl_get_error_message()), 0, $e);
+ throw new InvalidArgumentException(sprintf('Invalid message format (error #%d): '.intl_get_error_message(), intl_get_error_code()), 0, $e);
}
}
@@ -52,7 +52,7 @@ public function formatIntl(string $message, string $locale, array $parameters =
}
if (false === $message = $formatter->format($parameters)) {
- throw new InvalidArgumentException(sprintf('Unable to format message (error #%s): %s.', $formatter->getErrorCode(), $formatter->getErrorMessage()));
+ throw new InvalidArgumentException(sprintf('Unable to format message (error #%s): '.$formatter->getErrorMessage(), $formatter->getErrorCode()));
}
return $message;
diff --git a/src/Symfony/Component/Translation/Loader/JsonFileLoader.php b/src/Symfony/Component/Translation/Loader/JsonFileLoader.php
index fe6747e8aabd4..9f15dbc628893 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 aa36ebcc483ea..d7741a071007c 100644
--- a/src/Symfony/Component/Translation/Loader/XliffFileLoader.php
+++ b/src/Symfony/Component/Translation/Loader/XliffFileLoader.php
@@ -53,12 +53,12 @@ private function extract($resource, MessageCatalogue $catalogue, string $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 = XliffUtils::getVersionNumber($dom);
if ($errors = XliffUtils::validateSchema($dom)) {
- throw new InvalidResourceException(sprintf('Invalid resource provided: "%s"; Errors: %s.', $resource, XliffUtils::getErrorsAsString($errors)));
+ throw new InvalidResourceException(sprintf('Invalid resource provided: "%s"; Errors: '.XliffUtils::getErrorsAsString($errors), $resource));
}
if ('1.2' === $xliffVersion) {
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 3b3aba0450f85..e06f714bbd2ea 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/BicValidator.php b/src/Symfony/Component/Validator/Constraints/BicValidator.php
index c49986561e980..651d76dcf1c00 100644
--- a/src/Symfony/Component/Validator/Constraints/BicValidator.php
+++ b/src/Symfony/Component/Validator/Constraints/BicValidator.php
@@ -138,7 +138,7 @@ public function validate($value, Constraint $constraint)
try {
$iban = $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);
}
}
if (!$iban) {
diff --git a/src/Symfony/Component/Validator/Constraints/CallbackValidator.php b/src/Symfony/Component/Validator/Constraints/CallbackValidator.php
index ef1c10b181ed3..751c2b9c115ed 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.');
}
$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/RangeValidator.php b/src/Symfony/Component/Validator/Constraints/RangeValidator.php
index 9b7c40ce5ae9a..3cfd43f998676 100644
--- a/src/Symfony/Component/Validator/Constraints/RangeValidator.php
+++ b/src/Symfony/Component/Validator/Constraints/RangeValidator.php
@@ -157,7 +157,7 @@ private function getLimit($propertyPath, $default, Constraint $constraint)
try {
return $this->getPropertyAccessor()->getValue($object, $propertyPath);
} catch (NoSuchPropertyException $e) {
- throw new ConstraintDefinitionException(sprintf('Invalid property path "%s" provided to "%s" constraint: %s.', $propertyPath, \get_class($constraint), $e->getMessage()), 0, $e);
+ throw new ConstraintDefinitionException(sprintf('Invalid property path "%s" provided to "%s" constraint: '.$e->getMessage(), $propertyPath, \get_class($constraint)), 0, $e);
}
}
diff --git a/src/Symfony/Component/Validator/Constraints/UrlValidator.php b/src/Symfony/Component/Validator/Constraints/UrlValidator.php
index 43c21f5768118..77d7354946653 100644
--- a/src/Symfony/Component/Validator/Constraints/UrlValidator.php
+++ b/src/Symfony/Component/Validator/Constraints/UrlValidator.php
@@ -24,7 +24,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 c18cc0212e491..a197168bf72ab 100644
--- a/src/Symfony/Component/Validator/Context/ExecutionContext.php
+++ b/src/Symfony/Component/Validator/Context/ExecutionContext.php
@@ -21,6 +21,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;
use Symfony\Component\Validator\Violation\ConstraintViolationBuilderInterface;
@@ -192,7 +193,7 @@ public function addViolation($message, array $parameters = [])
$parameters,
$this->root,
$this->propertyPath,
- $this->value,
+ $this->getValue(),
null,
null,
$this->constraint
@@ -211,7 +212,7 @@ public function buildViolation($message, array $parameters = []): ConstraintViol
$parameters,
$this->root,
$this->propertyPath,
- $this->value,
+ $this->getValue(),
$this->translator,
$this->translationDomain
);
@@ -246,6 +247,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 38df595f5516c..642d8e6e9f0fb 100644
--- a/src/Symfony/Component/Validator/Mapping/Factory/LazyLoadingMetadataFactory.php
+++ b/src/Symfony/Component/Validator/Mapping/Factory/LazyLoadingMetadataFactory.php
@@ -138,34 +138,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 (\ReflectionClass $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 b076c13885dd0..3b3ee04509a12 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 c14c1be6b3264..82c224ff4d376 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(): string
{
@@ -37,6 +37,30 @@ public function getDefaultOption(): ?string
*/
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 6d98e64d77b3f..dde594d200e35 100644
--- a/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php
+++ b/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php
@@ -157,6 +157,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'],
@@ -255,6 +257,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',
]]),
];
@@ -209,6 +211,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
@@ -220,3 +231,15 @@ public function loadClassMetadata(ClassMetadata $metadata): bool
return true;
}
}
+
+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 c2838792421d1..6e659376a25bb 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;
use Symfony\Component\Validator\Validator\ValidatorInterface;
@@ -118,6 +123,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
index 622bcbd8c9c9f..b455441e33ad1 100644
--- a/src/Symfony/Component/Validator/Tests/Violation/ConstraintViolationBuilderTest.php
+++ b/src/Symfony/Component/Validator/Tests/Violation/ConstraintViolationBuilderTest.php
@@ -13,12 +13,98 @@
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\Tests\Fixtures\ConstraintA;
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());
+ }
+
/**
* @group legacy
* @expectedDeprecation Not using a string as the error code in Symfony\Component\Validator\Violation\ConstraintViolationBuilder::setCode() is deprecated since Symfony 4.4. A type-hint will be added in 5.0.
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 121ecf236467e..4497ea2989703 100644
--- a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php
+++ b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php
@@ -28,6 +28,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;
@@ -506,7 +507,13 @@ private function validateClassNode($object, ?string $cacheKey, ClassMetadataInte
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,
@@ -739,6 +746,10 @@ private function validateInGroup($value, ?string $cacheKey, MetadataInterface $m
$validator = $this->validatorFactory->getInstance($constraint);
$validator->initialize($context);
+ if ($value instanceof LazyProperty) {
+ $value = $value->getPropertyValue();
+ }
+
try {
$validator->validate($value, $constraint);
} catch (UnexpectedValueException $e) {
diff --git a/src/Symfony/Component/VarDumper/Server/DumpServer.php b/src/Symfony/Component/VarDumper/Server/DumpServer.php
index ad920bd4fca67..46546d1675998 100644
--- a/src/Symfony/Component/VarDumper/Server/DumpServer.php
+++ b/src/Symfony/Component/VarDumper/Server/DumpServer.php
@@ -41,7 +41,7 @@ public function __construct(string $host, LoggerInterface $logger = null)
public function start(): void
{
if (!$this->socket = stream_socket_server($this->host, $errno, $errstr)) {
- throw new \RuntimeException(sprintf('Server start failed on "%s": %s %s.', $this->host, $errstr, $errno));
+ throw new \RuntimeException(sprintf('Server start failed on "%s": '.$errstr.' '.$errno, $this->host));
}
}
diff --git a/src/Symfony/Component/VarExporter/Tests/InstantiatorTest.php b/src/Symfony/Component/VarExporter/Tests/InstantiatorTest.php
index 3da602383fc7c..6d0deb68dc9ac 100644
--- a/src/Symfony/Component/VarExporter/Tests/InstantiatorTest.php
+++ b/src/Symfony/Component/VarExporter/Tests/InstantiatorTest.php
@@ -29,7 +29,7 @@ public function testNotFoundClass()
public function testFailingInstantiation(string $class)
{
$this->expectException('Symfony\Component\VarExporter\Exception\NotInstantiableTypeException');
- $this->expectExceptionMessageRegExp('/Type ".*" is not instantiable\./');
+ $this->expectExceptionMessageMatches('/Type ".*" is not instantiable\./');
Instantiator::instantiate($class);
}
diff --git a/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php
index ec4cb4896980d..5fc46644c976a 100644
--- a/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php
+++ b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php
@@ -38,7 +38,7 @@ public function testPhpIncompleteClassesAreForbidden()
public function testFailingSerialization($value)
{
$this->expectException('Symfony\Component\VarExporter\Exception\NotInstantiableTypeException');
- $this->expectExceptionMessageRegExp('/Type ".*" is not instantiable\./');
+ $this->expectExceptionMessageMatches('/Type ".*" is not instantiable\./');
$expectedDump = $this->getDump($value);
try {
VarExporter::export($value);
diff --git a/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php b/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php
index 8c219f502bc3d..feb012913cf1a 100644
--- a/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php
+++ b/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php
@@ -56,7 +56,7 @@ public function getMarking($subject): Marking
$marking = $subject->{$method}();
- if (!$marking) {
+ if (null === $marking) {
return new Marking();
}
diff --git a/src/Symfony/Component/Workflow/MarkingStore/SingleStateMarkingStore.php b/src/Symfony/Component/Workflow/MarkingStore/SingleStateMarkingStore.php
index eca8a5d2a1449..fd279e967fcba 100644
--- a/src/Symfony/Component/Workflow/MarkingStore/SingleStateMarkingStore.php
+++ b/src/Symfony/Component/Workflow/MarkingStore/SingleStateMarkingStore.php
@@ -45,7 +45,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/MethodMarkingStoreTest.php b/src/Symfony/Component/Workflow/Tests/MarkingStore/MethodMarkingStoreTest.php
index 7393faa825752..155f285a4a976 100644
--- a/src/Symfony/Component/Workflow/Tests/MarkingStore/MethodMarkingStoreTest.php
+++ b/src/Symfony/Component/Workflow/Tests/MarkingStore/MethodMarkingStoreTest.php
@@ -53,6 +53,18 @@ public function testGetSetMarkingWithSingleState()
$this->assertEquals($marking, $marking2);
}
+ public function testGetSetMarkingWithSingleStateAndAlmostEmptyPlaceName()
+ {
+ $subject = new Subject(0);
+
+ $markingStore = new MethodMarkingStore(true);
+
+ $marking = $markingStore->getMarking($subject);
+
+ $this->assertInstanceOf(Marking::class, $marking);
+ $this->assertCount(1, $marking->getPlaces());
+ }
+
public function testGetMarkingWithValueObject()
{
$subject = new Subject($this->createValueObject('first_place'));
diff --git a/src/Symfony/Component/Workflow/Tests/MarkingStore/SingleStateMarkingStoreTest.php b/src/Symfony/Component/Workflow/Tests/MarkingStore/SingleStateMarkingStoreTest.php
index 7b640559ffff2..caaf977b72ec3 100644
--- a/src/Symfony/Component/Workflow/Tests/MarkingStore/SingleStateMarkingStoreTest.php
+++ b/src/Symfony/Component/Workflow/Tests/MarkingStore/SingleStateMarkingStoreTest.php
@@ -31,4 +31,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 afff1683e0a77..b975fb34ccfc8 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 bb24add32b001..0a99b0ba77b81 100644
--- a/src/Symfony/Component/Yaml/Inline.php
+++ b/src/Symfony/Component/Yaml/Inline.php
@@ -615,7 +615,7 @@ private static function evaluateScalar(string $scalar, int $flags, array $refere
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 7d25367cc8687..cc2b1e26fe6a4 100644
--- a/src/Symfony/Component/Yaml/Tests/DumperTest.php
+++ b/src/Symfony/Component/Yaml/Tests/DumperTest.php
@@ -223,6 +223,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"'],
diff --git a/src/Symfony/Component/Yaml/Tests/InlineTest.php b/src/Symfony/Component/Yaml/Tests/InlineTest.php
index fc3a1ede33d29..24c1b8a5de9b7 100644
--- a/src/Symfony/Component/Yaml/Tests/InlineTest.php
+++ b/src/Symfony/Component/Yaml/Tests/InlineTest.php
@@ -74,7 +74,7 @@ public function testParsePhpConstantThrowsExceptionWhenUndefined()
public function testParsePhpConstantThrowsExceptionOnInvalidType()
{
$this->expectException('Symfony\Component\Yaml\Exception\ParseException');
- $this->expectExceptionMessageRegExp('#The string "!php/const PHP_INT_MAX" could not be parsed as a constant.*#');
+ $this->expectExceptionMessageMatches('#The string "!php/const PHP_INT_MAX" could not be parsed as a constant.*#');
Inline::parse('!php/const PHP_INT_MAX', Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE);
}
@@ -599,7 +599,7 @@ public function getBinaryData()
public function testParseInvalidBinaryData($data, $expectedMessage)
{
$this->expectException('Symfony\Component\Yaml\Exception\ParseException');
- $this->expectExceptionMessageRegExp($expectedMessage);
+ $this->expectExceptionMessageMatches($expectedMessage);
Inline::parse($data);
}
@@ -787,7 +787,7 @@ public function phpConstTagWithEmptyValueProvider()
public function testUnquotedExclamationMarkThrows(string $value)
{
$this->expectException(ParseException::class);
- $this->expectExceptionMessageRegExp('/^Using the unquoted scalar value "!" is not supported\. You must quote it at line 1 \(near "/');
+ $this->expectExceptionMessageMatches('/^Using the unquoted scalar value "!" is not supported\. You must quote it at line 1 \(near "/');
Inline::parse($value);
}
diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php
index 4ed79466e6138..6caf8738855a0 100644
--- a/src/Symfony/Component/Yaml/Tests/ParserTest.php
+++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php
@@ -602,7 +602,7 @@ public function testShortcutKeyUnindentedCollectionException()
public function testMultipleDocumentsNotSupportedException()
{
$this->expectException('Symfony\Component\Yaml\Exception\ParseException');
- $this->expectExceptionMessageRegExp('/^Multiple documents are not supported.+/');
+ $this->expectExceptionMessageMatches('/^Multiple documents are not supported.+/');
Yaml::parse(<<<'EOL'
# Ranking of 1998 home runs
---
@@ -1348,7 +1348,7 @@ public function getBinaryData()
public function testParseInvalidBinaryData($data, $expectedMessage)
{
$this->expectException('Symfony\Component\Yaml\Exception\ParseException');
- $this->expectExceptionMessageRegExp($expectedMessage);
+ $this->expectExceptionMessageMatches($expectedMessage);
$this->parser->parse($data);
}
@@ -2102,14 +2102,14 @@ public function testParseFile()
public function testParsingNonExistentFilesThrowsException()
{
$this->expectException('Symfony\Component\Yaml\Exception\ParseException');
- $this->expectExceptionMessageRegExp('#^File ".+/Fixtures/nonexistent.yml" does not exist\.$#');
+ $this->expectExceptionMessageMatches('#^File ".+/Fixtures/nonexistent.yml" does not exist\.$#');
$this->parser->parseFile(__DIR__.'/Fixtures/nonexistent.yml');
}
public function testParsingNotReadableFilesThrowsException()
{
$this->expectException('Symfony\Component\Yaml\Exception\ParseException');
- $this->expectExceptionMessageRegExp('#^File ".+/Fixtures/not_readable.yml" cannot be read\.$#');
+ $this->expectExceptionMessageMatches('#^File ".+/Fixtures/not_readable.yml" cannot be read\.$#');
if ('\\' === \DIRECTORY_SEPARATOR) {
$this->markTestSkipped('chmod is not supported on Windows');
}