diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 58caff2209f37..00a686580d01f 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -14,8 +14,9 @@ This will help reviewers and should be a good start for the documentation.
Additionally (see https://symfony.com/releases):
- Always add tests and ensure they pass.
- Bug fixes must be submitted against the lowest maintained branch where they apply
- (lowest branches are regularly merged to upper ones so they get the fixes too.)
+ (lowest branches are regularly merged to upper ones so they get the fixes too).
- Features and deprecations must be submitted against the latest branch.
+ - For new features, provide some code snippets to help understand usage.
- Changelog entry should follow https://symfony.com/doc/current/contributing/code/conventions.html#writing-a-changelog-entry
- Never break backward compatibility (see https://symfony.com/bc).
-->
diff --git a/CHANGELOG-4.4.md b/CHANGELOG-4.4.md
index 9e6451685ab5c..b3498d37227df 100644
--- a/CHANGELOG-4.4.md
+++ b/CHANGELOG-4.4.md
@@ -7,6 +7,24 @@ 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.45 (2022-08-26)
+
+ * bug #47358 Fix broken request stack state if throwable is thrown. (Warxcell)
+ * bug #47304 [Serializer] Fix caching context-aware encoders/decoders in ChainEncoder/ChainDecoder (Guite)
+ * bug #47329 Email image parts: regex for single closing quote (rr-it)
+ * bug #47200 [Form] ignore missing keys when mapping DateTime objects to uninitialized arrays (xabbuh)
+ * bug #47189 [Validator] Add additional hint when `egulias/email-validator` needs to be installed (mpdude)
+ * bug #47195 [FrameworkBundle] fix writes to static $kernel property (xabbuh)
+ * bug #47175 [DowCrawler] Fix locale-sensitivity of whitespace normalization (nicolas-grekas)
+ * bug #47171 [TwigBridge] suggest to install the Twig bundle when the required component is already installed (xabbuh)
+ * bug #47161 [Mailer] Fix logic (fabpot)
+ * bug #47157 [Messenger] Fix Doctrine transport on MySQL (nicolas-grekas)
+ * bug #46190 [Translation] Fix translator overlapse (Xavier RENAUDIN)
+ * bug #47142 [Mailer] Fix error message in case of an STMP error (fabpot)
+ * bug #47145 [HttpClient] Fix shared connections not being freed on PHP < 8 (nicolas-grekas)
+ * bug #47143 [HttpClient] Fix memory leak when using StreamWrapper (nicolas-grekas)
+ * bug #47130 [HttpFoundation] Fix invalid ID not regenerated with native PHP file sessions (BrokenSourceCode)
+
* 4.4.44 (2022-07-29)
* bug #47069 [Security] Allow redirect after login to absolute URLs (Tim Ward)
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 99d8c121d9fce..167d559dc9e9f 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -24,8 +24,8 @@ The Symfony Connect username in parenthesis allows to get more information
- Yonel Ceruto (yonelceruto)
- Tobias Nyholm (tobias)
- Oskar Stark (oskarstark)
- - Ryan Weaver (weaverryan)
- Javier Eguiluz (javier.eguiluz)
+ - Ryan Weaver (weaverryan)
- Johannes S (johannes)
- Jakub Zalas (jakubzalas)
- Kris Wallsmith (kriswallsmith)
@@ -43,9 +43,9 @@ The Symfony Connect username in parenthesis allows to get more information
- Martin Hasoň (hason)
- Jérôme Tamarelle (gromnan)
- Jeremy Mikola (jmikola)
+ - Kevin Bond (kbond)
- Jean-François Simon (jfsimon)
- Benjamin Eberlei (beberlei)
- - Kevin Bond (kbond)
- Igor Wiedler
- Valentin Udaltsov (vudaltsov)
- Vasilij Duško (staff)
@@ -84,8 +84,8 @@ The Symfony Connect username in parenthesis allows to get more information
- Sarah Khalil (saro0h)
- Konstantin Kudryashov (everzet)
- Vincent Langlet (deviling)
- - Bilal Amarni (bamarni)
- Tomas Norkūnas (norkunas)
+ - Bilal Amarni (bamarni)
- Eriksen Costa
- Florin Patan (florinpatan)
- Peter Rehm (rpet)
@@ -95,14 +95,14 @@ The Symfony Connect username in parenthesis allows to get more information
- Julien Falque (julienfalque)
- Massimiliano Arione (garak)
- Douglas Greenshields (shieldo)
- - Christian Raue
- David Buchmann (dbu)
+ - Christian Raue
+ - Jáchym Toušek (enumag)
- Graham Campbell (graham)
- Michel Weimerskirch (mweimerskirch)
- Eric Clemmons (ericclemmons)
- Issei Murasawa (issei_m)
- Fran Moreno (franmomu)
- - Jáchym Toušek (enumag)
- Malte Schlüter (maltemaltesich)
- Mathias Arlaud (mtarld)
- Vasilij Dusko
@@ -114,8 +114,8 @@ The Symfony Connect username in parenthesis allows to get more information
- Dariusz Górecki (canni)
- Maxime Helias (maxhelias)
- Ener-Getick
- - Sebastiaan Stok (sstok)
- Ruud Kamphuis (ruudk)
+ - Sebastiaan Stok (sstok)
- Jérôme Vasseur (jvasseur)
- Ion Bazan (ionbazan)
- Lee McDermott
@@ -134,6 +134,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Konstantin.Myakshin
- Rokas Mikalkėnas (rokasm)
- Arman Hosseini (arman)
+ - Antoine Lamirault
- Arnaud Le Blanc (arnaud-lb)
- Maxime STEINHAUSSER
- Peter Kokot (maastermedia)
@@ -146,10 +147,10 @@ The Symfony Connect username in parenthesis allows to get more information
- YaFou
- Gary PEGEOT (gary-p)
- Chris Wilkinson (thewilkybarkid)
+ - Mathieu Lechat (mat_the_cat)
- Brice BERNARD (brikou)
- Roman Martinuk (a2a4)
- Gregor Harlan (gharlan)
- - Antoine Lamirault
- Baptiste Clavié (talus)
- Adrien Brault (adrienbrault)
- Michal Piotrowski
@@ -161,7 +162,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Włodzimierz Gajda (gajdaw)
- Christian Scheb
- Guillaume (guill)
- - Mathieu Lechat (mat_the_cat)
- Tugdual Saunier (tucksaun)
- Jacob Dreesen (jdreesen)
- Joel Wurtz (brouznouf)
@@ -173,6 +173,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Javier Spagnoletti (phansys)
- excelwebzone
- Jérôme Parmentier (lctrs)
+ - Jeroen Spee (jeroens)
- HeahDude
- Richard van Laak (rvanlaak)
- Paráda József (paradajozsef)
@@ -180,7 +181,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Alexander Schwenn (xelaris)
- Fabien Pennequin (fabienpennequin)
- Gordon Franke (gimler)
- - Jeroen Spee (jeroens)
- Christopher Hertel (chertel)
- Gabriel Caruso
- Anthony GRASSIOT (antograssiot)
@@ -207,6 +207,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Jhonny Lidfors (jhonne)
- Martin Hujer (martinhujer)
- Wouter J
+ - Guilliam Xavier
- Timo Bakx (timobakx)
- Juti Noppornpitak (shiroyuki)
- Joe Bennett (kralos)
@@ -215,6 +216,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Colin O'Dell (colinodell)
- Sebastian Hörl (blogsh)
- Ben Davies (bendavies)
+ - Andreas Schempp (aschempp)
- François-Xavier de Guillebon (de-gui_f)
- Daniel Gomes (danielcsgomes)
- Michael Käfer (michael_kaefer)
@@ -223,11 +225,10 @@ The Symfony Connect username in parenthesis allows to get more information
- Arnaud Kleinpeter (nanocom)
- Guilherme Blanco (guilhermeblanco)
- Chi-teck
- - Guilliam Xavier
+ - Antonio Pauletich (x-coder264)
- Nate Wiebe (natewiebe13)
- Michael Voříšek
- SpacePossum
- - Andreas Schempp (aschempp)
- Pablo Godel (pgodel)
- Romaric Drigon (romaricdrigon)
- Andréia Bohner (andreia)
@@ -239,7 +240,6 @@ The Symfony Connect username in parenthesis allows to get more information
- jwdeitch
- Jurica Vlahoviček (vjurica)
- David Prévot
- - Antonio Pauletich (x-coder264)
- Vincent Touzet (vincenttouzet)
- Fabien Bourigault (fbourigault)
- Farhad Safarov (safarov)
@@ -373,9 +373,11 @@ The Symfony Connect username in parenthesis allows to get more information
- Emanuele Panzeri (thepanz)
- Matthew Smeets
- François Zaninotto (fzaninotto)
+ - Alexis Lefebvre
- Dustin Whittle (dustinwhittle)
- jeff
- John Kary (johnkary)
+ - Bob van de Vijver (bobvandevijver)
- smoench
- Michele Orselli (orso)
- Sven Paulus (subsven)
@@ -397,8 +399,10 @@ The Symfony Connect username in parenthesis allows to get more information
- Fabien S (bafs)
- Victor Bocharsky (bocharsky_bw)
- Sébastien Alfaiate (seb33300)
+ - Jan Sorgalla (jsor)
- henrikbjorn
- Alex Bowers
+ - Simon Podlipsky (simpod)
- Marcel Beerta (mazen)
- Phil Taylor (prazgod)
- flack (flack)
@@ -426,7 +430,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Iker Ibarguren (ikerib)
- Manuel Reinhard (sprain)
- Johann Pardanaud
- - Alexis Lefebvre
- Indra Gunawan (indragunawan)
- Tim Goudriaan (codedmonkey)
- Harm van Tilborg (hvt)
@@ -444,7 +447,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Xavier Montaña Carreras (xmontana)
- Tarmo Leppänen (tarlepp)
- AnneKir
- - Bob van de Vijver (bobvandevijver)
- Tobias Weichart
- Miro Michalicka
- M. Vondano
@@ -473,12 +475,10 @@ The Symfony Connect username in parenthesis allows to get more information
- Félix Labrecque (woodspire)
- GordonsLondon
- Roman Anasal
- - Jan Sorgalla (jsor)
- Piotr Kugla (piku235)
- Quynh Xuan Nguyen (seriquynh)
- Ray
- Philipp Cordes (corphi)
- - Simon Podlipsky (simpod)
- Chekote
- bhavin (bhavin4u)
- Pavel Popov (metaer)
@@ -495,12 +495,14 @@ The Symfony Connect username in parenthesis allows to get more information
- Thomas Schulz (king2500)
- Benjamin Morel
- Bernd Stellwag
+ - Romain Monteil (ker0x)
- Frank de Jonge
- Chris Tanaskoski
- julien57
- Loïc Frémont (loic425)
- Ben Ramsey (ramsey)
- Matthieu Auger (matthieuauger)
+ - Kévin THERAGE (kevin_therage)
- Josip Kruslin (jkruslin)
- Giorgio Premi
- renanbr
@@ -526,6 +528,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Michael Holm (hollo)
- Giso Stallenberg (gisostallenberg)
- Blanchon Vincent (blanchonvincent)
+ - William Arslett (warslett)
- Christian Schmidt
- Gonzalo Vilaseca (gonzalovilaseca)
- Vadim Borodavko (javer)
@@ -575,7 +578,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Marko Kaznovac (kaznovac)
- Emanuele Gaspari (inmarelibero)
- Dariusz Rumiński
- - Romain Monteil (ker0x)
- Terje Bråten
- Gennadi Janzen
- James Hemery
@@ -599,7 +601,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Fractal Zombie
- Gunnstein Lye (glye)
- Thomas Talbot (ioni)
- - Kévin THERAGE (kevin_therage)
- Noémi Salaün (noemi-salaun)
- Michel Hunziker
- Krystian Marcisz (simivar)
@@ -623,6 +624,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Thomas Royer (cydonia7)
- Gildas Quéméner (gquemener)
- Nicolas LEFEVRE (nicoweb)
+ - Asmir Mustafic (goetas)
- Martins Sipenko
- Guilherme Augusto Henschel
- Mardari Dorel (dorumd)
@@ -662,7 +664,6 @@ The Symfony Connect username in parenthesis allows to get more information
- “Filip
- Simon Watiau (simonwatiau)
- Ruben Jacobs (rubenj)
- - William Arslett
- Arkadius Stefanski (arkadius)
- Jérémy M (th3mouk)
- Terje Bråten
@@ -700,6 +701,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Philipp Kräutli (pkraeutli)
- Carl Casbolt (carlcasbolt)
- battye
+ - BrokenSourceCode
- Grzegorz (Greg) Zdanowski (kiler129)
- Kirill chEbba Chebunin
- kylekatarnls (kylekatarnls)
@@ -850,7 +852,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Arturs Vonda
- Xavier Briand (xavierbriand)
- Daniel Badura
- - Asmir Mustafic (goetas)
- vagrant
- Asier Illarramendi (doup)
- AKeeman (akeeman)
@@ -861,6 +862,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Chris Sedlmayr (catchamonkey)
- Kamil Kokot (pamil)
- Seb Koelen
+ - FORT Pierre-Louis (plfort)
- Christoph Mewes (xrstf)
- Vitaliy Tverdokhlib (vitaliytv)
- Ariel Ferrandini (aferrandini)
@@ -900,7 +902,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Pablo Díez (pablodip)
- Damien Fa
- Kevin McBride
- - BrokenSourceCode
- Sergio Santoro
- Philipp Rieber (bicpi)
- Dennis Væversted (srnzitcom)
@@ -988,6 +989,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Ziumin
- Matthias Schmidt
- Lenar Lõhmus
+ - Samaël Villette (samadu61)
- Zach Badgett (zachbadgett)
- Loïc Faugeron
- Aurélien Fredouelle
@@ -1049,6 +1051,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Simon DELICATA
- Thibault Buathier (gwemox)
- vitaliytv
+ - Andreas Hennings
- Arnaud Frézet
- Nicolas Martin (cocorambo)
- luffy1727
@@ -1136,6 +1139,7 @@ The Symfony Connect username in parenthesis allows to get more information
- David Fuhr
- Evgeny Anisiforov
- TristanPouliquen
+ - Gwendolen Lynch
- mwos
- Aurimas Niekis (gcds)
- Volker Killesreiter (ol0lll)
@@ -1189,7 +1193,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Chris Heng (gigablah)
- Oleksii Svitiashchuk
- Tristan Bessoussa (sf_tristanb)
- - FORT Pierre-Louis (plfort)
- Richard Bradley
- Nathanaël Martel (nathanaelmartel)
- Nicolas Jourdan (nicolasjc)
@@ -1259,6 +1262,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Aleksandr Dankovtsev
- Maciej Zgadzaj
- David Legatt (dlegatt)
+ - Maarten de Boer (mdeboer)
- Cameron Porter
- Hossein Bukhamsin
- Oliver Hoff
@@ -1463,6 +1467,7 @@ The Symfony Connect username in parenthesis allows to get more information
- bill moll
- PaoRuby
- Bizley
+ - Edvin Hultberg
- Dominik Piekarski (dompie)
- Rares Sebastian Moldovan (raresmldvn)
- Felds Liscia (felds)
@@ -1773,7 +1778,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Pierre-Olivier Vares (povares)
- Ronny López (ronnylt)
- Julius (sakalys)
- - Samaël Villette (samadu61)
- abdul malik ikhsan (samsonasik)
- Dmitry (staratel)
- Tito Miguel Costa (titomiguelcosta)
@@ -1956,6 +1960,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Daniel Alejandro Castro Arellano (lexcast)
- Aleksandar Dimitrov (netbull)
- Gary Houbre (thegarious)
+ - Florent Morselli
- Thomas Jarrand
- Baptiste Leduc (bleduc)
- Antoine Bluchet (soyuka)
@@ -2053,6 +2058,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Pablo Borowicz
- Máximo Cuadros (mcuadros)
- Lukas Mencl
+ - EXT - THERAGE Kevin
- tamirvs
- gauss
- julien.galenski
@@ -2067,6 +2073,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Goran Juric
- Laurent G. (laurentg)
- Nicolas Macherey
+ - Asil Barkin Elik (asilelik)
- Bhujagendra Ishaya
- Guido Donnari
- Mert Simsek (mrtsmsk0)
@@ -2097,6 +2104,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Dan Finnie
- Ken Marfilla (marfillaster)
- Max Grigorian (maxakawizard)
+ - allison guilhem
- benatespina (benatespina)
- Denis Kop
- Jean-Guilhem Rouel (jean-gui)
@@ -2130,6 +2138,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Tadas Gliaubicas (tadcka)
- Thanos Polymeneas (thanos)
- Benoit Garret
+ - HellFirePvP
- Maximilian Ruta (deltachaos)
- Jakub Sacha
- Olaf Klischat
@@ -2194,6 +2203,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Philipp Kretzschmar
- Ilya Vertakov
- Brooks Boyd
+ - Axel Venet
- Roger Webb
- Dmitriy Simushev
- Pawel Smolinski
@@ -2201,7 +2211,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Oxan van Leeuwen
- pkowalczyk
- Soner Sayakci
- - Andreas Hennings
- Max Voloshin (maxvoloshin)
- Nicolas Fabre (nfabre)
- Raul Rodriguez (raul782)
@@ -2238,6 +2247,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Dmitri Petmanson
- heccjj
- Alexandre Melard
+ - AlbinoDrought
- Jay Klehr
- Sergey Yuferev
- Tobias Stöckler
@@ -2247,6 +2257,7 @@ The Symfony Connect username in parenthesis allows to get more information
- cilefen (cilefen)
- Mo Di (modi)
- Pablo Schläpfer
+ - Xavier RENAUDIN
- Christian Wahler (christian)
- Jelte Steijaert (jelte)
- David Négrier (moufmouf)
@@ -2268,6 +2279,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Malaney J. Hill
- Patryk Kozłowski
- Alexandre Pavy
+ - Tim Ward
- Christian Flach (cmfcmf)
- Lars Ambrosius Wallenborn (larsborn)
- Oriol Mangas Abellan (oriolman)
@@ -2282,6 +2294,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Mihai Nica (redecs)
- Andrei Igna
- azine
+ - Wojciech Zimoń
- Pierre Tachoire
- Dawid Sajdak
- Ludek Stepan
@@ -2363,6 +2376,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Daniel Kay (danielkay-cp)
- Matt Daum (daum)
- Alberto Pirovano (geezmo)
+ - Pascal Woerde (pascalwoerde)
- Pete Mitchell (peterjmit)
- Tom Corrigan (tomcorrigan)
- Luis Galeas
@@ -2479,6 +2493,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Keith Maika
- Mephistofeles
- Hoffmann András
+ - Cédric Anne
- LubenZA
- Flavian Sierk
- Michael Bessolov
@@ -2508,7 +2523,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Alex Teterin (errogaht)
- Gunnar Lium (gunnarlium)
- Malte Wunsch (maltewunsch)
- - Maarten de Boer (mdeboer)
- Tiago Garcia (tiagojsag)
- Artiom
- Jakub Simon
@@ -2635,6 +2649,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Andy Stanberry
- Felix Marezki
- Normunds
+ - Walter Doekes
- Thomas Rothe
- Troy Crawford
- nietonfir
@@ -2952,7 +2967,6 @@ The Symfony Connect username in parenthesis allows to get more information
- temperatur
- Paul Andrieux
- Cas
- - Gwendolen Lynch
- ghazy ben ahmed
- Karolis
- Myke79
@@ -3051,6 +3065,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Shude
- Ondřej Führer
- Sema
+ - Ayke Halder
- Thorsten Hallwas
- Brian Freytag
- Alex Nostadt
@@ -3062,7 +3077,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Yuriy Potemkin
- Emilie Lorenzo
- enomotodev
- - Edvin Hultberg
- Vincent
- Benjamin Long
- Ben Miller
diff --git a/src/Symfony/Bridge/Twig/UndefinedCallableHandler.php b/src/Symfony/Bridge/Twig/UndefinedCallableHandler.php
index 16381fddac6c8..43aff010e3c48 100644
--- a/src/Symfony/Bridge/Twig/UndefinedCallableHandler.php
+++ b/src/Symfony/Bridge/Twig/UndefinedCallableHandler.php
@@ -11,6 +11,7 @@
namespace Symfony\Bridge\Twig;
+use Composer\InstalledVersions;
use Symfony\Bundle\FullStack;
use Twig\Error\SyntaxError;
@@ -93,6 +94,12 @@ private static function onUndefined(string $name, string $type, string $componen
throw new SyntaxError(sprintf('Did you forget to %s? Unknown %s "%s".', self::FULL_STACK_ENABLE[$component], $type, $name));
}
- throw new SyntaxError(sprintf('Did you forget to run "composer require symfony/%s"? Unknown %s "%s".', $component, $type, $name));
+ $missingPackage = 'symfony/'.$component;
+
+ if (class_exists(InstalledVersions::class) && InstalledVersions::isInstalled($missingPackage)) {
+ $missingPackage = 'symfony/twig-bundle';
+ }
+
+ throw new SyntaxError(sprintf('Did you forget to run "composer require %s"? Unknown %s "%s".', $missingPackage, $type, $name));
}
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.php
index 33a214ea01aa5..50b51f90734c1 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.php
@@ -53,11 +53,6 @@ protected function configure()
Before running this command, the cache must be empty.
-This command does not generate the classes cache (as when executing this
-command, too many classes that should be part of the cache are already loaded
-in memory). Use curl or any other similar tool to warm up
-the classes cache if you want.
-
EOF
)
;
diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php
index 1ec6548be2738..5df1cc87df7af 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php
@@ -77,7 +77,7 @@ protected static function bootKernel(array $options = [])
$kernel = static::createKernel($options);
$kernel->boot();
- self::$kernel = $kernel;
+ static::$kernel = $kernel;
static::$booted = true;
$container = static::$kernel->getContainer();
diff --git a/src/Symfony/Component/Console/Descriptor/ApplicationDescription.php b/src/Symfony/Component/Console/Descriptor/ApplicationDescription.php
index 3970b90007369..91b18460582fb 100644
--- a/src/Symfony/Component/Console/Descriptor/ApplicationDescription.php
+++ b/src/Symfony/Component/Console/Descriptor/ApplicationDescription.php
@@ -131,7 +131,7 @@ private function sortCommands(array $commands): array
}
if ($namespacedCommands) {
- ksort($namespacedCommands);
+ ksort($namespacedCommands, \SORT_STRING);
foreach ($namespacedCommands as $key => $commandsSet) {
ksort($commandsSet);
$sortedCommands[$key] = $commandsSet;
diff --git a/src/Symfony/Component/Console/Tests/Descriptor/ApplicationDescriptionTest.php b/src/Symfony/Component/Console/Tests/Descriptor/ApplicationDescriptionTest.php
index 33d5c3840f3e3..f1408d087d5e7 100644
--- a/src/Symfony/Component/Console/Tests/Descriptor/ApplicationDescriptionTest.php
+++ b/src/Symfony/Component/Console/Tests/Descriptor/ApplicationDescriptionTest.php
@@ -36,7 +36,7 @@ public function getNamespacesProvider()
return [
[['_global'], ['foobar']],
[['a', 'b'], ['b:foo', 'a:foo', 'b:bar']],
- [['_global', 'b', 'z', 22, 33], ['z:foo', '1', '33:foo', 'b:foo', '22:foo:bar']],
+ [['_global', 22, 33, 'b', 'z'], ['z:foo', '1', '33:foo', 'b:foo', '22:foo:bar']],
];
}
}
diff --git a/src/Symfony/Component/DomCrawler/Crawler.php b/src/Symfony/Component/DomCrawler/Crawler.php
index 4f89eec75a74b..ec8e023ec1d05 100644
--- a/src/Symfony/Component/DomCrawler/Crawler.php
+++ b/src/Symfony/Component/DomCrawler/Crawler.php
@@ -620,7 +620,7 @@ public function text(/* string $default = null, bool $normalizeWhitespace = true
$text = $this->getNode(0)->nodeValue;
if (\func_num_args() <= 1) {
- if (trim(preg_replace('/(?:\s{2,}+|[^\S ])/', ' ', $text)) !== $text) {
+ if (trim(preg_replace("/(?:[ \n\r\t\x0C]{2,}+|[\n\r\t\x0C])/", ' ', $text), " \n\r\t\x0C") !== $text) {
@trigger_error(sprintf('"%s()" will normalize whitespaces by default in Symfony 5.0, set the second "$normalizeWhitespace" argument to false to retrieve the non-normalized version of the text.', __METHOD__), \E_USER_DEPRECATED);
}
@@ -628,7 +628,7 @@ public function text(/* string $default = null, bool $normalizeWhitespace = true
}
if (\func_num_args() > 1 && func_get_arg(1)) {
- return trim(preg_replace('/(?:\s{2,}+|[^\S ])/', ' ', $text));
+ return trim(preg_replace("/(?:[ \n\r\t\x0C]{2,}+|[\n\r\t\x0C])/", ' ', $text), " \n\r\t\x0C");
}
return $text;
diff --git a/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php b/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php
index 2a78e03f89715..edd58b83096ec 100644
--- a/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php
+++ b/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php
@@ -14,6 +14,7 @@
use Symfony\Component\Form\DataMapperInterface;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\PropertyAccess\Exception\AccessException;
+use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException;
use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
@@ -96,6 +97,10 @@ private function getPropertyValue($data, $propertyPath)
try {
return $this->propertyAccessor->getValue($data, $propertyPath);
} catch (AccessException $e) {
+ if (\is_array($data) && $e instanceof NoSuchIndexException) {
+ return null;
+ }
+
if (!$e instanceof UninitializedPropertyException
// For versions without UninitializedPropertyException check the exception message
&& (class_exists(UninitializedPropertyException::class) || !str_contains($e->getMessage(), 'You should initialize it'))
diff --git a/src/Symfony/Component/Form/Tests/CompoundFormTest.php b/src/Symfony/Component/Form/Tests/CompoundFormTest.php
index fcfd8fe9e520f..5f6cc60654d62 100644
--- a/src/Symfony/Component/Form/Tests/CompoundFormTest.php
+++ b/src/Symfony/Component/Form/Tests/CompoundFormTest.php
@@ -15,6 +15,7 @@
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Form\Exception\AlreadySubmittedException;
use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper;
+use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationRequestHandler;
@@ -36,6 +37,7 @@
use Symfony\Component\Form\Tests\Fixtures\Map;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\PropertyAccess\PropertyAccess;
class CompoundFormTest extends TestCase
{
@@ -1076,6 +1078,30 @@ public function testFileUpload()
$this->assertNull($this->form->get('bar')->getData());
}
+ public function testMapDateTimeObjectsWithEmptyArrayData()
+ {
+ $propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()
+ ->enableExceptionOnInvalidIndex()
+ ->getPropertyAccessor();
+ $form = $this->factory->createBuilder()
+ ->setDataMapper(new PropertyPathMapper($propertyAccessor))
+ ->add('date', DateType::class, [
+ 'auto_initialize' => false,
+ 'format' => 'dd/MM/yyyy',
+ 'html5' => false,
+ 'model_timezone' => 'UTC',
+ 'view_timezone' => 'UTC',
+ 'widget' => 'single_text',
+ ])
+ ->getForm();
+
+ $form->submit([
+ 'date' => '04/08/2022',
+ ]);
+
+ $this->assertEquals(['date' => new \DateTime('2022-08-04', new \DateTimeZone('UTC'))], $form->getData());
+ }
+
private function createForm(string $name = 'name', bool $compound = true): FormInterface
{
$builder = $this->getBuilder($name);
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php
index 76d936d9789a6..5d729eff971ec 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php
@@ -15,8 +15,10 @@
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper;
+use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormConfigBuilder;
+use Symfony\Component\Form\FormFactoryBuilder;
use Symfony\Component\Form\Tests\Fixtures\TypehintedPropertiesCar;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
@@ -362,6 +364,30 @@ public function provideDate()
[new \DateTimeImmutable()],
];
}
+
+ public function testMapFormsToDataMapsDateTimeInstanceToArrayIfNotSetBefore()
+ {
+ $propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()
+ ->enableExceptionOnInvalidIndex()
+ ->getPropertyAccessor();
+ $form = (new FormFactoryBuilder())->getFormFactory()->createBuilder()
+ ->setDataMapper(new PropertyPathMapper($propertyAccessor))
+ ->add('date', DateType::class, [
+ 'auto_initialize' => false,
+ 'format' => 'dd/MM/yyyy',
+ 'html5' => false,
+ 'model_timezone' => 'UTC',
+ 'view_timezone' => 'UTC',
+ 'widget' => 'single_text',
+ ])
+ ->getForm();
+
+ $form->submit([
+ 'date' => '04/08/2022',
+ ]);
+
+ $this->assertEquals(['date' => new \DateTime('2022-08-04', new \DateTimeZone('UTC'))], $form->getData());
+ }
}
class SubmittedForm extends Form
diff --git a/src/Symfony/Component/HttpClient/Internal/CurlClientState.php b/src/Symfony/Component/HttpClient/Internal/CurlClientState.php
index 5821f67a6f700..904cc47b90669 100644
--- a/src/Symfony/Component/HttpClient/Internal/CurlClientState.php
+++ b/src/Symfony/Component/HttpClient/Internal/CurlClientState.php
@@ -96,7 +96,7 @@ public function reset()
curl_share_setopt($this->share, \CURLSHOPT_SHARE, \CURL_LOCK_DATA_DNS);
curl_share_setopt($this->share, \CURLSHOPT_SHARE, \CURL_LOCK_DATA_SSL_SESSION);
- if (\defined('CURL_LOCK_DATA_CONNECT')) {
+ if (\defined('CURL_LOCK_DATA_CONNECT') && \PHP_VERSION_ID >= 80000) {
curl_share_setopt($this->share, \CURLSHOPT_SHARE, \CURL_LOCK_DATA_CONNECT);
}
}
diff --git a/src/Symfony/Component/HttpClient/Response/StreamWrapper.php b/src/Symfony/Component/HttpClient/Response/StreamWrapper.php
index 644f2ee190f57..6bb31687b949c 100644
--- a/src/Symfony/Component/HttpClient/Response/StreamWrapper.php
+++ b/src/Symfony/Component/HttpClient/Response/StreamWrapper.php
@@ -53,20 +53,18 @@ public static function createResource(ResponseInterface $response, HttpClientInt
throw new \InvalidArgumentException(sprintf('Providing a client to "%s()" is required when the response doesn\'t have any "stream()" method.', __CLASS__));
}
- if (false === stream_wrapper_register('symfony', __CLASS__)) {
+ static $registered = false;
+
+ if (!$registered = $registered || stream_wrapper_register(strtr(__CLASS__, '\\', '-'), __CLASS__)) {
throw new \RuntimeException(error_get_last()['message'] ?? 'Registering the "symfony" stream wrapper failed.');
}
- try {
- $context = [
- 'client' => $client ?? $response,
- 'response' => $response,
- ];
-
- return fopen('symfony://'.$response->getInfo('url'), 'r', false, stream_context_create(['symfony' => $context])) ?: null;
- } finally {
- stream_wrapper_unregister('symfony');
- }
+ $context = [
+ 'client' => $client ?? $response,
+ 'response' => $response,
+ ];
+
+ return fopen(strtr(__CLASS__, '\\', '-').'://'.$response->getInfo('url'), 'r', false, stream_context_create(['symfony' => $context]));
}
public function getResponse(): ResponseInterface
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/StrictSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/StrictSessionHandler.php
index 627bcfa1dfa84..7e5b5c019cea3 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/StrictSessionHandler.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/StrictSessionHandler.php
@@ -30,6 +30,16 @@ public function __construct(\SessionHandlerInterface $handler)
$this->handler = $handler;
}
+ /**
+ * Returns true if this handler wraps an internal PHP session save handler using \SessionHandler.
+ *
+ * @internal
+ */
+ public function isWrapper(): bool
+ {
+ return $this->handler instanceof \SessionHandler;
+ }
+
/**
* @return bool
*/
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/SessionHandlerProxy.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/SessionHandlerProxy.php
index 6539acf989387..0defa4a7ab19a 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/SessionHandlerProxy.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/SessionHandlerProxy.php
@@ -11,6 +11,8 @@
namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler;
+
/**
* @author Drak
*/
@@ -22,7 +24,7 @@ public function __construct(\SessionHandlerInterface $handler)
{
$this->handler = $handler;
$this->wrapper = $handler instanceof \SessionHandler;
- $this->saveHandlerName = $this->wrapper ? \ini_get('session.save_handler') : 'user';
+ $this->saveHandlerName = $this->wrapper || ($handler instanceof StrictSessionHandler && $handler->isWrapper()) ? \ini_get('session.save_handler') : 'user';
}
/**
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_max_age.expected b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_max_age.expected
index 6870a27728bbe..c4b31d4f3e7af 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_max_age.expected
+++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_max_age.expected
@@ -6,6 +6,6 @@ Array
[0] => Content-Type: text/plain; charset=utf-8
[1] => Cache-Control: no-cache, private
[2] => Date: Sat, 12 Nov 1955 20:04:00 GMT
- [3] => Set-Cookie: foo=bar; expires=Sat, 01-Jan-10000 02:46:40 GMT; Max-Age=%d; path=/
+ [3] => Set-Cookie: foo=bar; expires=Sat, 01 Jan 10000 02:46:40 GMT; Max-Age=%d; path=/
)
shutdown
diff --git a/src/Symfony/Component/HttpFoundation/Tests/ResponseFunctionalTest.php b/src/Symfony/Component/HttpFoundation/Tests/ResponseFunctionalTest.php
index 471455d708753..aa24291eda5dc 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/ResponseFunctionalTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/ResponseFunctionalTest.php
@@ -48,6 +48,7 @@ public function testCookie($fixture)
}
$result = file_get_contents(sprintf('http://localhost:8054/%s.php', $fixture));
+ $result = preg_replace_callback('/expires=[^;]++/', function ($m) { return str_replace('-', ' ', $m[0]); }, $result);
$this->assertStringMatchesFormatFile(__DIR__.sprintf('/Fixtures/response-functional/%s.expected', $fixture), $result);
}
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php
index f6417720d27aa..aca2bfd882b20 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php
@@ -46,6 +46,7 @@ public function testSession($fixture)
$context = ['http' => ['header' => "Cookie: sid=123abc\r\n"]];
$context = stream_context_create($context);
$result = file_get_contents(sprintf('http://localhost:8053/%s.php', $fixture), false, $context);
+ $result = preg_replace_callback('/expires=[^;]++/', function ($m) { return str_replace('-', ' ', $m[0]); }, $result);
$this->assertStringEqualsFile(__DIR__.sprintf('/Fixtures/%s.expected', $fixture), $result);
}
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.expected b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.expected
index 8203714740752..06a118888aba9 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.expected
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.expected
@@ -12,6 +12,6 @@ Array
(
[0] => Content-Type: text/plain; charset=utf-8
[1] => Cache-Control: max-age=10800, private, must-revalidate
- [2] => Set-Cookie: sid=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; secure; HttpOnly
+ [2] => Set-Cookie: sid=deleted; expires=Thu, 01 Jan 1970 00:00:01 GMT; Max-Age=0; path=/; secure; HttpOnly
)
shutdown
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Fixtures/storage.expected b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Fixtures/storage.expected
index 05a5d5d0b090f..549c6847f11da 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Fixtures/storage.expected
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Fixtures/storage.expected
@@ -16,6 +16,6 @@ Array
(
[0] => Content-Type: text/plain; charset=utf-8
[1] => Cache-Control: max-age=0, private, must-revalidate
- [2] => Set-Cookie: sid=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; secure; HttpOnly
+ [2] => Set-Cookie: sid=deleted; expires=Thu, 01 Jan 1970 00:00:01 GMT; Max-Age=0; path=/; secure; HttpOnly
)
shutdown
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.expected b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.expected
index 63078228df139..ac8ec061f0310 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.expected
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.expected
@@ -20,6 +20,6 @@ Array
[0] => Content-Type: text/plain; charset=utf-8
[1] => Cache-Control: max-age=10800, private, must-revalidate
[2] => Set-Cookie: abc=def
- [3] => Set-Cookie: sid=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; secure; HttpOnly
+ [3] => Set-Cookie: sid=deleted; expires=Thu, 01 Jan 1970 00:00:01 GMT; Max-Age=0; path=/; secure; HttpOnly
)
shutdown
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php
index 972a2745132e1..a4f45fec68708 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php
@@ -12,6 +12,8 @@
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Proxy;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler;
+use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
/**
@@ -159,6 +161,23 @@ public function testUpdateTimestamp()
$this->proxy->updateTimestamp('id', 'data');
}
+
+ /**
+ * @dataProvider provideNativeSessionStorageHandler
+ */
+ public function testNativeSessionStorageSaveHandlerName($handler)
+ {
+ $this->assertSame('files', (new NativeSessionStorage([], $handler))->getSaveHandler()->getSaveHandlerName());
+ }
+
+ public function provideNativeSessionStorageHandler()
+ {
+ return [
+ [new \SessionHandler()],
+ [new StrictSessionHandler(new \SessionHandler())],
+ [new SessionHandlerProxy(new StrictSessionHandler(new \SessionHandler()))],
+ ];
+ }
}
abstract class TestSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface
diff --git a/src/Symfony/Component/HttpKernel/HttpKernel.php b/src/Symfony/Component/HttpKernel/HttpKernel.php
index 0ed82d777b1c4..d53b80665b467 100644
--- a/src/Symfony/Component/HttpKernel/HttpKernel.php
+++ b/src/Symfony/Component/HttpKernel/HttpKernel.php
@@ -76,6 +76,7 @@ public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQ
{
$request->headers->set('X-Php-Ob-Level', (string) ob_get_level());
+ $this->requestStack->push($request);
try {
return $this->handleRaw($request, $type);
} catch (\Exception $e) {
@@ -89,6 +90,8 @@ public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQ
}
return $this->handleThrowable($e, $request, $type);
+ } finally {
+ $this->requestStack->pop();
}
}
@@ -127,8 +130,6 @@ public function terminateWithException(\Throwable $exception, Request $request =
*/
private function handleRaw(Request $request, int $type = self::MASTER_REQUEST): Response
{
- $this->requestStack->push($request);
-
// request
$event = new RequestEvent($this, $request, $type);
$this->dispatcher->dispatch($event, KernelEvents::REQUEST);
@@ -205,7 +206,6 @@ private function filterResponse(Response $response, Request $request, int $type)
private function finishRequest(Request $request, int $type)
{
$this->dispatcher->dispatch(new FinishRequestEvent($this, $request, $type), KernelEvents::FINISH_REQUEST);
- $this->requestStack->pop();
}
/**
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index 978324f29fb6a..adb9a94727c5e 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 = [];
- public const VERSION = '4.4.44';
- public const VERSION_ID = 40444;
+ public const VERSION = '4.4.45';
+ public const VERSION_ID = 40445;
public const MAJOR_VERSION = 4;
public const MINOR_VERSION = 4;
- public const RELEASE_VERSION = 44;
+ public const RELEASE_VERSION = 45;
public const EXTRA_VERSION = '';
public const END_OF_MAINTENANCE = '11/2022';
diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php
index 014dc752c32ae..53e5f547d249f 100644
--- a/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php
@@ -39,6 +39,45 @@ public function testHandleWhenControllerThrowsAnExceptionAndCatchIsTrue()
$kernel->handle(new Request(), HttpKernelInterface::MASTER_REQUEST, true);
}
+ public function testRequestStackIsNotBrokenWhenControllerThrowsAnExceptionAndCatchIsTrue()
+ {
+ $requestStack = new RequestStack();
+ $kernel = $this->getHttpKernel(new EventDispatcher(), function () { throw new \RuntimeException(); }, $requestStack);
+
+ try {
+ $kernel->handle(new Request(), HttpKernelInterface::MASTER_REQUEST, true);
+ } catch (\Throwable $exception) {
+ }
+
+ self::assertNull($requestStack->getCurrentRequest());
+ }
+
+ public function testRequestStackIsNotBrokenWhenControllerThrowsAnExceptionAndCatchIsFalse()
+ {
+ $requestStack = new RequestStack();
+ $kernel = $this->getHttpKernel(new EventDispatcher(), function () { throw new \RuntimeException(); }, $requestStack);
+
+ try {
+ $kernel->handle(new Request(), HttpKernelInterface::MASTER_REQUEST, false);
+ } catch (\Throwable $exception) {
+ }
+
+ self::assertNull($requestStack->getCurrentRequest());
+ }
+
+ public function testRequestStackIsNotBrokenWhenControllerThrowsAnThrowable()
+ {
+ $requestStack = new RequestStack();
+ $kernel = $this->getHttpKernel(new EventDispatcher(), function () { throw new \Error(); }, $requestStack);
+
+ try {
+ $kernel->handle(new Request(), HttpKernelInterface::MASTER_REQUEST, true);
+ } catch (\Throwable $exception) {
+ }
+
+ self::assertNull($requestStack->getCurrentRequest());
+ }
+
public function testHandleWhenControllerThrowsAnExceptionAndCatchIsFalseAndNoListenerIsRegistered()
{
$this->expectException(\RuntimeException::class);
diff --git a/src/Symfony/Component/Mailer/Transport/Smtp/EsmtpTransport.php b/src/Symfony/Component/Mailer/Transport/Smtp/EsmtpTransport.php
index e3c30487ca6c4..25006fbb7d326 100644
--- a/src/Symfony/Component/Mailer/Transport/Smtp/EsmtpTransport.php
+++ b/src/Symfony/Component/Mailer/Transport/Smtp/EsmtpTransport.php
@@ -94,15 +94,7 @@ public function addAuthenticator(AuthenticatorInterface $authenticator): void
protected function doHeloCommand(): void
{
- try {
- $response = $this->executeCommand(sprintf("EHLO %s\r\n", $this->getLocalDomain()), [250]);
- } catch (TransportExceptionInterface $e) {
- parent::doHeloCommand();
-
- return;
- }
-
- $capabilities = $this->getCapabilities($response);
+ $capabilities = $this->callHeloCommand();
/** @var SocketStream $stream */
$stream = $this->getStream();
@@ -116,14 +108,7 @@ protected function doHeloCommand(): void
throw new TransportException('Unable to connect with STARTTLS.');
}
- try {
- $response = $this->executeCommand(sprintf("EHLO %s\r\n", $this->getLocalDomain()), [250]);
- $capabilities = $this->getCapabilities($response);
- } catch (TransportExceptionInterface $e) {
- parent::doHeloCommand();
-
- return;
- }
+ $capabilities = $this->callHeloCommand();
}
if (\array_key_exists('AUTH', $capabilities)) {
@@ -131,10 +116,24 @@ protected function doHeloCommand(): void
}
}
- private function getCapabilities(string $ehloResponse): array
+ private function callHeloCommand(): array
{
+ try {
+ $response = $this->executeCommand(sprintf("EHLO %s\r\n", $this->getLocalDomain()), [250]);
+ } catch (TransportExceptionInterface $e) {
+ try {
+ parent::doHeloCommand();
+ } catch (TransportExceptionInterface $ex) {
+ if (!$ex->getCode()) {
+ throw $e;
+ }
+
+ throw $ex;
+ }
+ }
+
$capabilities = [];
- $lines = explode("\r\n", trim($ehloResponse));
+ $lines = explode("\r\n", trim($response));
array_shift($lines);
foreach ($lines as $line) {
if (preg_match('/^[0-9]{3}[ -]([A-Z0-9-]+)((?:[ =].*)?)$/Di', $line, $matches)) {
diff --git a/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php b/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php
index e4c2ec215ed88..3c05e94e376d1 100644
--- a/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php
+++ b/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php
@@ -295,15 +295,13 @@ private function assertResponseCode(string $response, array $codes): void
throw new LogicException('You must set the expected response code.');
}
- if (!$response) {
- throw new TransportException(sprintf('Expected response code "%s" but got an empty response.', implode('/', $codes)));
- }
-
[$code] = sscanf($response, '%3d');
$valid = \in_array($code, $codes);
- if (!$valid) {
- throw new TransportException(sprintf('Expected response code "%s" but got code "%s", with message "%s".', implode('/', $codes), $code, trim($response)), $code);
+ if (!$valid || !$response) {
+ $codeStr = $code ? sprintf('code "%s"', $code) : 'empty code';
+ $responseStr = $response ? sprintf(', with message "%s"', trim($response)) : '';
+ throw new TransportException(sprintf('Expected response code "%s" but got ', implode('/', $codes), $codeStr).$codeStr.$responseStr.'.', $code);
}
}
diff --git a/src/Symfony/Component/Messenger/Transport/Doctrine/Connection.php b/src/Symfony/Component/Messenger/Transport/Doctrine/Connection.php
index 83434752d91ec..57bf5346ef55d 100644
--- a/src/Symfony/Component/Messenger/Transport/Doctrine/Connection.php
+++ b/src/Symfony/Component/Messenger/Transport/Doctrine/Connection.php
@@ -162,7 +162,7 @@ public function get(): ?array
{
if ($this->driverConnection->getDatabasePlatform() instanceof MySQLPlatform) {
try {
- $this->driverConnection->delete($this->configuration['table_name'], ['delivered_at' => '9999-12-31']);
+ $this->driverConnection->delete($this->configuration['table_name'], ['delivered_at' => '9999-12-31 23:59:59']);
} catch (DriverException $e) {
// Ignore the exception
}
@@ -252,7 +252,7 @@ public function ack(string $id): bool
{
try {
if ($this->driverConnection->getDatabasePlatform() instanceof MySQLPlatform) {
- return $this->driverConnection->update($this->configuration['table_name'], ['delivered_at' => '9999-12-31'], ['id' => $id]) > 0;
+ return $this->driverConnection->update($this->configuration['table_name'], ['delivered_at' => '9999-12-31 23:59:59'], ['id' => $id]) > 0;
}
return $this->driverConnection->delete($this->configuration['table_name'], ['id' => $id]) > 0;
@@ -265,7 +265,7 @@ public function reject(string $id): bool
{
try {
if ($this->driverConnection->getDatabasePlatform() instanceof MySQLPlatform) {
- return $this->driverConnection->update($this->configuration['table_name'], ['delivered_at' => '9999-12-31'], ['id' => $id]) > 0;
+ return $this->driverConnection->update($this->configuration['table_name'], ['delivered_at' => '9999-12-31 23:59:59'], ['id' => $id]) > 0;
}
return $this->driverConnection->delete($this->configuration['table_name'], ['id' => $id]) > 0;
diff --git a/src/Symfony/Component/Mime/Email.php b/src/Symfony/Component/Mime/Email.php
index 29da5ef2fdcb9..9cdde13e533c6 100644
--- a/src/Symfony/Component/Mime/Email.php
+++ b/src/Symfony/Component/Mime/Email.php
@@ -503,7 +503,7 @@ private function prepareParts(): ?array
$html = stream_get_contents($html);
}
$htmlPart = new TextPart($html, $this->htmlCharset, 'html');
- preg_match_all('(
]*src\s*=\s*(?:([\'"])cid:([^"]+)\\1|cid:([^>\s]+)))i', $html, $names);
+ preg_match_all('(
]*src\s*=\s*(?:([\'"])cid:(.+?)\\1|cid:([^>\s]+)))i', $html, $names);
$names = array_filter(array_unique(array_merge($names[2], $names[3])));
}
diff --git a/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php b/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php
index 2a55d93a6066f..29c656397f1f0 100644
--- a/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php
+++ b/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php
@@ -67,9 +67,13 @@ private function getDecoder(string $format, array $context): DecoderInterface
return $this->decoders[$this->decoderByFormat[$format]];
}
+ $cache = true;
foreach ($this->decoders as $i => $decoder) {
+ $cache = $cache && !$decoder instanceof ContextAwareDecoderInterface;
if ($decoder->supportsDecoding($format, $context)) {
- $this->decoderByFormat[$format] = $i;
+ if ($cache) {
+ $this->decoderByFormat[$format] = $i;
+ }
return $decoder;
}
diff --git a/src/Symfony/Component/Serializer/Encoder/ChainEncoder.php b/src/Symfony/Component/Serializer/Encoder/ChainEncoder.php
index b13333e88aabb..c2c9e3c8ad2e2 100644
--- a/src/Symfony/Component/Serializer/Encoder/ChainEncoder.php
+++ b/src/Symfony/Component/Serializer/Encoder/ChainEncoder.php
@@ -85,9 +85,13 @@ private function getEncoder(string $format, array $context): EncoderInterface
return $this->encoders[$this->encoderByFormat[$format]];
}
+ $cache = true;
foreach ($this->encoders as $i => $encoder) {
+ $cache = $cache && !$encoder instanceof ContextAwareEncoderInterface;
if ($encoder->supportsEncoding($format, $context)) {
- $this->encoderByFormat[$format] = $i;
+ if ($cache) {
+ $this->encoderByFormat[$format] = $i;
+ }
return $encoder;
}
diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/ChainDecoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/ChainDecoderTest.php
index 5cac8d99a5270..8f433ce0fa15a 100644
--- a/src/Symfony/Component/Serializer/Tests/Encoder/ChainDecoderTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Encoder/ChainDecoderTest.php
@@ -13,6 +13,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\Serializer\Encoder\ChainDecoder;
+use Symfony\Component\Serializer\Encoder\ContextAwareDecoderInterface;
use Symfony\Component\Serializer\Encoder\DecoderInterface;
use Symfony\Component\Serializer\Exception\RuntimeException;
@@ -28,7 +29,7 @@ class ChainDecoderTest extends TestCase
protected function setUp(): void
{
- $this->decoder1 = $this->createMock(DecoderInterface::class);
+ $this->decoder1 = $this->createMock(ContextAwareDecoderInterface::class);
$this->decoder1
->method('supportsDecoding')
->willReturnMap([
@@ -36,6 +37,7 @@ protected function setUp(): void
[self::FORMAT_2, [], false],
[self::FORMAT_3, [], false],
[self::FORMAT_3, ['foo' => 'bar'], true],
+ [self::FORMAT_3, ['foo' => 'bar2'], false],
]);
$this->decoder2 = $this->createMock(DecoderInterface::class);
@@ -45,6 +47,8 @@ protected function setUp(): void
[self::FORMAT_1, [], false],
[self::FORMAT_2, [], true],
[self::FORMAT_3, [], false],
+ [self::FORMAT_3, ['foo' => 'bar'], false],
+ [self::FORMAT_3, ['foo' => 'bar2'], true],
]);
$this->chainDecoder = new ChainDecoder([$this->decoder1, $this->decoder2]);
@@ -52,10 +56,26 @@ protected function setUp(): void
public function testSupportsDecoding()
{
+ $this->decoder1
+ ->method('decode')
+ ->willReturn('result1');
+ $this->decoder2
+ ->method('decode')
+ ->willReturn('result2');
+
$this->assertTrue($this->chainDecoder->supportsDecoding(self::FORMAT_1));
+ $this->assertEquals('result1', $this->chainDecoder->decode('', self::FORMAT_1, []));
+
$this->assertTrue($this->chainDecoder->supportsDecoding(self::FORMAT_2));
+ $this->assertEquals('result2', $this->chainDecoder->decode('', self::FORMAT_2, []));
+
$this->assertFalse($this->chainDecoder->supportsDecoding(self::FORMAT_3));
+
$this->assertTrue($this->chainDecoder->supportsDecoding(self::FORMAT_3, ['foo' => 'bar']));
+ $this->assertEquals('result1', $this->chainDecoder->decode('', self::FORMAT_3, ['foo' => 'bar']));
+
+ $this->assertTrue($this->chainDecoder->supportsDecoding(self::FORMAT_3, ['foo' => 'bar2']));
+ $this->assertEquals('result2', $this->chainDecoder->decode('', self::FORMAT_3, ['foo' => 'bar2']));
}
public function testDecode()
diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/ChainEncoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/ChainEncoderTest.php
index e80dc4f1843a6..8ae9e38c1337a 100644
--- a/src/Symfony/Component/Serializer/Tests/Encoder/ChainEncoderTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Encoder/ChainEncoderTest.php
@@ -13,6 +13,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\Serializer\Encoder\ChainEncoder;
+use Symfony\Component\Serializer\Encoder\ContextAwareEncoderInterface;
use Symfony\Component\Serializer\Encoder\EncoderInterface;
use Symfony\Component\Serializer\Encoder\NormalizationAwareInterface;
use Symfony\Component\Serializer\Exception\RuntimeException;
@@ -29,7 +30,7 @@ class ChainEncoderTest extends TestCase
protected function setUp(): void
{
- $this->encoder1 = $this->createMock(EncoderInterface::class);
+ $this->encoder1 = $this->createMock(ContextAwareEncoderInterface::class);
$this->encoder1
->method('supportsEncoding')
->willReturnMap([
@@ -37,6 +38,7 @@ protected function setUp(): void
[self::FORMAT_2, [], false],
[self::FORMAT_3, [], false],
[self::FORMAT_3, ['foo' => 'bar'], true],
+ [self::FORMAT_3, ['foo' => 'bar2'], false],
]);
$this->encoder2 = $this->createMock(EncoderInterface::class);
@@ -46,6 +48,8 @@ protected function setUp(): void
[self::FORMAT_1, [], false],
[self::FORMAT_2, [], true],
[self::FORMAT_3, [], false],
+ [self::FORMAT_3, ['foo' => 'bar'], false],
+ [self::FORMAT_3, ['foo' => 'bar2'], true],
]);
$this->chainEncoder = new ChainEncoder([$this->encoder1, $this->encoder2]);
@@ -53,10 +57,26 @@ protected function setUp(): void
public function testSupportsEncoding()
{
+ $this->encoder1
+ ->method('encode')
+ ->willReturn('result1');
+ $this->encoder2
+ ->method('encode')
+ ->willReturn('result2');
+
$this->assertTrue($this->chainEncoder->supportsEncoding(self::FORMAT_1));
+ $this->assertEquals('result1', $this->chainEncoder->encode('', self::FORMAT_1, []));
+
$this->assertTrue($this->chainEncoder->supportsEncoding(self::FORMAT_2));
+ $this->assertEquals('result2', $this->chainEncoder->encode('', self::FORMAT_2, []));
+
$this->assertFalse($this->chainEncoder->supportsEncoding(self::FORMAT_3));
+
$this->assertTrue($this->chainEncoder->supportsEncoding(self::FORMAT_3, ['foo' => 'bar']));
+ $this->assertEquals('result1', $this->chainEncoder->encode('', self::FORMAT_3, ['foo' => 'bar']));
+
+ $this->assertTrue($this->chainEncoder->supportsEncoding(self::FORMAT_3, ['foo' => 'bar2']));
+ $this->assertEquals('result2', $this->chainEncoder->encode('', self::FORMAT_3, ['foo' => 'bar2']));
}
public function testEncode()
diff --git a/src/Symfony/Component/Translation/MessageCatalogue.php b/src/Symfony/Component/Translation/MessageCatalogue.php
index 6e6b9fe0eaa95..b43b22d6370f3 100644
--- a/src/Symfony/Component/Translation/MessageCatalogue.php
+++ b/src/Symfony/Component/Translation/MessageCatalogue.php
@@ -159,19 +159,14 @@ public function replace($messages, $domain = 'messages')
*/
public function add($messages, $domain = 'messages')
{
- if (!isset($this->messages[$domain])) {
- $this->messages[$domain] = [];
- }
- $intlDomain = $domain;
- if (!str_ends_with($domain, self::INTL_DOMAIN_SUFFIX)) {
- $intlDomain .= self::INTL_DOMAIN_SUFFIX;
- }
+ $altDomain = str_ends_with($domain, self::INTL_DOMAIN_SUFFIX) ? substr($domain, 0, -\strlen(self::INTL_DOMAIN_SUFFIX)) : $domain.self::INTL_DOMAIN_SUFFIX;
foreach ($messages as $id => $message) {
- if (isset($this->messages[$intlDomain]) && \array_key_exists($id, $this->messages[$intlDomain])) {
- $this->messages[$intlDomain][$id] = $message;
- } else {
- $this->messages[$domain][$id] = $message;
- }
+ unset($this->messages[$altDomain][$id]);
+ $this->messages[$domain][$id] = $message;
+ }
+
+ if ([] === ($this->messages[$altDomain] ?? null)) {
+ unset($this->messages[$altDomain]);
}
}
diff --git a/src/Symfony/Component/Translation/Tests/TranslatorTest.php b/src/Symfony/Component/Translation/Tests/TranslatorTest.php
index 073f2255fe7f4..6c9bc7a176664 100644
--- a/src/Symfony/Component/Translation/Tests/TranslatorTest.php
+++ b/src/Symfony/Component/Translation/Tests/TranslatorTest.php
@@ -15,6 +15,10 @@
use Symfony\Component\Translation\Exception\InvalidArgumentException;
use Symfony\Component\Translation\Exception\NotFoundResourceException;
use Symfony\Component\Translation\Exception\RuntimeException;
+use Symfony\Component\Translation\Formatter\IntlFormatter;
+use Symfony\Component\Translation\Formatter\IntlFormatterInterface;
+use Symfony\Component\Translation\Formatter\MessageFormatter;
+use Symfony\Component\Translation\Formatter\MessageFormatterInterface;
use Symfony\Component\Translation\Loader\ArrayLoader;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\Translator;
@@ -683,6 +687,26 @@ public function testIntlFormattedDomain()
$this->assertSame('Hi Bob', $translator->trans('some_message', ['%name%' => 'Bob']));
}
+ public function testIntlDomainOverlapseWithIntlResourceBefore()
+ {
+ $intlFormatterMock = $this->createMock(IntlFormatterInterface::class);
+ $intlFormatterMock->expects($this->once())->method('formatIntl')->with('hello intl', 'en', [])->willReturn('hello intl');
+
+ $messageFormatter = new MessageFormatter(null, $intlFormatterMock);
+
+ $translator = new Translator('en', $messageFormatter);
+ $translator->addLoader('array', new ArrayLoader());
+
+ $translator->addResource('array', ['some_message' => 'hello intl'], 'en', 'messages+intl-icu');
+ $translator->addResource('array', ['some_message' => 'hello'], 'en', 'messages');
+
+ $this->assertSame('hello', $translator->trans('some_message', [], 'messages'));
+
+ $translator->addResource('array', ['some_message' => 'hello intl'], 'en', 'messages+intl-icu');
+
+ $this->assertSame('hello intl', $translator->trans('some_message', [], 'messages'));
+ }
+
/**
* @group legacy
*/
diff --git a/src/Symfony/Component/Validator/Constraints/EmailValidator.php b/src/Symfony/Component/Validator/Constraints/EmailValidator.php
index c5a4b21ef21b3..8ea5cee68a2c8 100644
--- a/src/Symfony/Component/Validator/Constraints/EmailValidator.php
+++ b/src/Symfony/Component/Validator/Constraints/EmailValidator.php
@@ -100,6 +100,10 @@ public function validate($value, Constraint $constraint)
}
if (null === $constraint->mode) {
+ if (Email::VALIDATION_MODE_STRICT === $this->defaultMode && !class_exists(EguliasEmailValidator::class)) {
+ throw new LogicException(sprintf('The "egulias/email-validator" component is required to make the "%s" constraint default to strict mode.', EguliasEmailValidator::class));
+ }
+
$constraint->mode = $this->defaultMode;
}
diff --git a/src/Symfony/Component/Yaml/Tests/DumperTest.php b/src/Symfony/Component/Yaml/Tests/DumperTest.php
index 2ebbbd047313c..59a47a8130f31 100644
--- a/src/Symfony/Component/Yaml/Tests/DumperTest.php
+++ b/src/Symfony/Component/Yaml/Tests/DumperTest.php
@@ -14,6 +14,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\Yaml\Dumper;
use Symfony\Component\Yaml\Exception\DumpException;
+use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Parser;
use Symfony\Component\Yaml\Tag\TaggedValue;
use Symfony\Component\Yaml\Yaml;
@@ -78,7 +79,8 @@ public function testIndentationInConstructor()
- foo
EOF;
- $this->assertEquals($expected, $dumper->dump($this->array, 4, 0));
+ $this->assertSame($expected, $dumper->dump($this->array, 4, 0));
+ $this->assertSameData($this->array, $this->parser->parse($expected));
}
public function testSpecifications()
@@ -94,14 +96,17 @@ public function testSpecifications()
}
$test = $this->parser->parse($yaml);
- if (isset($test['dump_skip']) && $test['dump_skip']) {
+ if ($test['dump_skip'] ?? false) {
continue;
- } elseif (isset($test['todo']) && $test['todo']) {
+ }
+
+ if ($test['todo'] ?? false) {
// TODO
- } else {
- eval('$expected = '.trim($test['php']).';');
- $this->assertSame($expected, $this->parser->parse($this->dumper->dump($expected, 10)), $test['test']);
+ continue;
}
+
+ $expected = eval('return '.trim($test['php']).';');
+ $this->assertSame($expected, $this->parser->parse($this->dumper->dump($expected, 10)), $test['test']);
}
}
}
@@ -111,8 +116,9 @@ public function testInlineLevel()
$expected = <<<'EOF'
{ '': bar, foo: '#bar', 'foo''bar': { }, bar: [1, foo, { a: A }], foobar: { foo: bar, bar: [1, foo], foobar: { foo: bar, bar: [1, foo] } } }
EOF;
- $this->assertEquals($expected, $this->dumper->dump($this->array, -10), '->dump() takes an inline level argument');
- $this->assertEquals($expected, $this->dumper->dump($this->array, 0), '->dump() takes an inline level argument');
+ $this->assertSame($expected, $this->dumper->dump($this->array, -10), '->dump() takes an inline level argument');
+ $this->assertSame($expected, $this->dumper->dump($this->array, 0), '->dump() takes an inline level argument');
+ $this->assertSameData($this->array, $this->parser->parse($expected));
$expected = <<<'EOF'
'': bar
@@ -122,7 +128,8 @@ public function testInlineLevel()
foobar: { foo: bar, bar: [1, foo], foobar: { foo: bar, bar: [1, foo] } }
EOF;
- $this->assertEquals($expected, $this->dumper->dump($this->array, 1), '->dump() takes an inline level argument');
+ $this->assertSame($expected, $this->dumper->dump($this->array, 1), '->dump() takes an inline level argument');
+ $this->assertSameData($this->array, $this->parser->parse($expected));
$expected = <<<'EOF'
'': bar
@@ -138,7 +145,8 @@ public function testInlineLevel()
foobar: { foo: bar, bar: [1, foo] }
EOF;
- $this->assertEquals($expected, $this->dumper->dump($this->array, 2), '->dump() takes an inline level argument');
+ $this->assertSame($expected, $this->dumper->dump($this->array, 2), '->dump() takes an inline level argument');
+ $this->assertSameData($this->array, $this->parser->parse($expected));
$expected = <<<'EOF'
'': bar
@@ -159,7 +167,8 @@ public function testInlineLevel()
bar: [1, foo]
EOF;
- $this->assertEquals($expected, $this->dumper->dump($this->array, 3), '->dump() takes an inline level argument');
+ $this->assertSame($expected, $this->dumper->dump($this->array, 3), '->dump() takes an inline level argument');
+ $this->assertSameData($this->array, $this->parser->parse($expected));
$expected = <<<'EOF'
'': bar
@@ -182,22 +191,23 @@ public function testInlineLevel()
- foo
EOF;
- $this->assertEquals($expected, $this->dumper->dump($this->array, 4), '->dump() takes an inline level argument');
- $this->assertEquals($expected, $this->dumper->dump($this->array, 10), '->dump() takes an inline level argument');
+ $this->assertSame($expected, $this->dumper->dump($this->array, 4), '->dump() takes an inline level argument');
+ $this->assertSame($expected, $this->dumper->dump($this->array, 10), '->dump() takes an inline level argument');
+ $this->assertSameData($this->array, $this->parser->parse($expected));
}
public function testObjectSupportEnabled()
{
$dump = $this->dumper->dump(['foo' => new A(), 'bar' => 1], 0, 0, Yaml::DUMP_OBJECT);
- $this->assertEquals('{ foo: !php/object \'O:30:"Symfony\Component\Yaml\Tests\A":1:{s:1:"a";s:3:"foo";}\', bar: 1 }', $dump, '->dump() is able to dump objects');
+ $this->assertSame('{ foo: !php/object \'O:30:"Symfony\Component\Yaml\Tests\A":1:{s:1:"a";s:3:"foo";}\', bar: 1 }', $dump, '->dump() is able to dump objects');
}
public function testObjectSupportDisabledButNoExceptions()
{
$dump = $this->dumper->dump(['foo' => new A(), 'bar' => 1]);
- $this->assertEquals('{ foo: null, bar: 1 }', $dump, '->dump() does not dump objects when disabled');
+ $this->assertSame('{ foo: null, bar: 1 }', $dump, '->dump() does not dump objects when disabled');
}
public function testObjectSupportDisabledWithExceptions()
@@ -211,7 +221,8 @@ public function testObjectSupportDisabledWithExceptions()
*/
public function testEscapedEscapeSequencesInQuotedScalar($input, $expected)
{
- $this->assertEquals($expected, $this->dumper->dump($input));
+ $this->assertSame($expected, $this->dumper->dump($input));
+ $this->assertSameData($input, $this->parser->parse($expected));
}
public function getEscapeSequences()
@@ -261,7 +272,7 @@ public function testDumpObjectAsMap($object, $expected)
{
$yaml = $this->dumper->dump($object, 0, 0, Yaml::DUMP_OBJECT_AS_MAP);
- $this->assertEquals($expected, Yaml::parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP));
+ $this->assertSameData($expected, $this->parser->parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP));
}
public function objectAsMapProvider()
@@ -339,7 +350,7 @@ public function testDumpingArrayObjectInstancesWithNumericKeysRespectsInlineLeve
2: { 0: d, 1: e }
YAML;
- $this->assertEquals($expected, $yaml);
+ $this->assertSame($expected, $yaml);
}
public function testDumpEmptyArrayObjectInstanceAsMap()
@@ -378,6 +389,7 @@ public function testDumpingStdClassInstancesRespectsInlineLevel()
YAML;
$this->assertSame($expected, $yaml);
+ $this->assertSameData($outer, $this->parser->parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP));
}
public function testDumpingTaggedValueSequenceRespectsInlineLevel()
@@ -403,6 +415,59 @@ public function testDumpingTaggedValueSequenceRespectsInlineLevel()
YAML;
$this->assertSame($expected, $yaml);
+ $this->assertSameData($data, $this->parser->parse($expected, Yaml::PARSE_CUSTOM_TAGS));
+ }
+
+ public function testDumpingTaggedValueTopLevelScalar()
+ {
+ $data = new TaggedValue('user', 'jane');
+
+ $yaml = $this->dumper->dump($data);
+
+ $expected = '!user jane';
+ $this->assertSame($expected, $yaml);
+ $this->assertSameData($data, $this->parser->parse($yaml, Yaml::PARSE_CUSTOM_TAGS));
+ }
+
+ public function testDumpingTaggedValueTopLevelAssocInline()
+ {
+ $data = new TaggedValue('user', ['name' => 'jane']);
+
+ $yaml = $this->dumper->dump($data);
+
+ $expected = '!user { name: jane }';
+ $this->assertSame($expected, $yaml);
+ $this->assertSameData($data, $this->parser->parse($yaml, Yaml::PARSE_CUSTOM_TAGS));
+ }
+
+ public function testDumpingTaggedValueTopLevelAssoc()
+ {
+ $data = new TaggedValue('user', ['name' => 'jane']);
+
+ // @todo Fix the dumper, the output should not be ''.
+ $expected = '';
+ $yaml = $this->dumper->dump($data, 2);
+ $this->assertSame($expected, $yaml);
+ }
+
+ public function testDumpingTaggedValueTopLevelMultiLine()
+ {
+ $data = new TaggedValue('text', "a\nb\n");
+
+ // @todo Fix the dumper, the output should not be ''.
+ $expected = '';
+ $this->assertSame($expected, $this->dumper->dump($data, 2, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK));
+ }
+
+ public function testDumpingTaggedValueSpecialCharsInTag()
+ {
+ // @todo Validate the tag name in the TaggedValue constructor.
+ $data = new TaggedValue('a b @ c', 5);
+ $expected = '!a b @ c 5';
+ $this->assertSame($expected, $this->dumper->dump($data));
+ // The data changes after a round trip, due to the illegal tag name.
+ $data = new TaggedValue('a', 'b @ c 5');
+ $this->assertSameData($data, $this->parser->parse($expected, Yaml::PARSE_CUSTOM_TAGS));
}
public function testDumpingTaggedValueSequenceWithInlinedTagValues()
@@ -415,6 +480,7 @@ public function testDumpingTaggedValueSequenceWithInlinedTagValues()
'john',
'claire',
]),
+ new TaggedValue('number', 5),
];
$yaml = $this->dumper->dump($data, 1);
@@ -422,9 +488,13 @@ public function testDumpingTaggedValueSequenceWithInlinedTagValues()
$expected = <<assertSame($expected, $yaml);
+ // @todo Fix the parser, preserve numbers.
+ $data[2] = new TaggedValue('number', '5');
+ $this->assertSameData($data, $this->parser->parse($expected, Yaml::PARSE_CUSTOM_TAGS));
}
public function testDumpingTaggedValueMapRespectsInlineLevel()
@@ -437,6 +507,7 @@ public function testDumpingTaggedValueMapRespectsInlineLevel()
'john',
'claire',
]),
+ 'count' => new TaggedValue('number', 5),
];
$yaml = $this->dumper->dump($data, 2);
@@ -447,9 +518,13 @@ public function testDumpingTaggedValueMapRespectsInlineLevel()
names1: !names
- john
- claire
+count: !number 5
YAML;
$this->assertSame($expected, $yaml);
+ // @todo Fix the parser, preserve numbers.
+ $data['count'] = new TaggedValue('number', '5');
+ $this->assertSameData($data, $this->parser->parse($expected, Yaml::PARSE_CUSTOM_TAGS));
}
public function testDumpingTaggedValueMapWithInlinedTagValues()
@@ -472,6 +547,7 @@ public function testDumpingTaggedValueMapWithInlinedTagValues()
YAML;
$this->assertSame($expected, $yaml);
+ $this->assertSameData($data, $this->parser->parse($expected, Yaml::PARSE_CUSTOM_TAGS));
}
public function testDumpingNotInlinedScalarTaggedValue()
@@ -487,6 +563,7 @@ public function testDumpingNotInlinedScalarTaggedValue()
YAML;
$this->assertSame($expected, $this->dumper->dump($data, 2));
+ $this->assertSameData($data, $this->parser->parse($expected, Yaml::PARSE_CUSTOM_TAGS));
}
public function testDumpingNotInlinedNullTaggedValue()
@@ -500,6 +577,10 @@ public function testDumpingNotInlinedNullTaggedValue()
YAML;
$this->assertSame($expected, $this->dumper->dump($data, 2));
+
+ // @todo Fix the parser, don't stringify null.
+ $data['foo'] = new TaggedValue('bar', 'null');
+ $this->assertSameData($data, $this->parser->parse($expected, Yaml::PARSE_CUSTOM_TAGS | Yaml::PARSE_CONSTANT));
}
public function testDumpingMultiLineStringAsScalarBlockTaggedValue()
@@ -519,6 +600,53 @@ public function testDumpingMultiLineStringAsScalarBlockTaggedValue()
' baz';
$this->assertSame($expected, $this->dumper->dump($data, 2, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK));
+ $this->assertSameData($data, $this->parser->parse($expected, Yaml::PARSE_CUSTOM_TAGS));
+ }
+
+ public function testDumpingTaggedMultiLineInList()
+ {
+ $data = [
+ new TaggedValue('bar', "a\nb"),
+ ];
+ $expected = "- !bar |\n a\n b";
+ $this->assertSame($expected, $this->dumper->dump($data, 2, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK));
+
+ // @todo Fix the parser, eliminate these exceptions.
+ $this->expectException(ParseException::class);
+ $this->expectExceptionMessage('Unable to parse at line 3 (near "!bar |").');
+
+ $this->parser->parse($expected, Yaml::PARSE_CUSTOM_TAGS);
+ }
+
+ public function testDumpingTaggedMultiLineTrailingNewlinesInMap()
+ {
+ $data = [
+ 'foo' => new TaggedValue('bar', "a\nb\n\n\n"),
+ ];
+ $expected = "foo: !bar |\n a\n b\n \n \n ";
+ $this->assertSame($expected, $this->dumper->dump($data, 2, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK));
+
+ // @todo Fix the parser, the result should be identical to $data.
+ $this->assertSameData(
+ [
+ 'foo' => new TaggedValue('bar', "a\nb\n"),
+ ],
+ $this->parser->parse($expected, Yaml::PARSE_CUSTOM_TAGS));
+ }
+
+ public function testDumpingTaggedMultiLineTrailingNewlinesInList()
+ {
+ $data = [
+ new TaggedValue('bar', "a\nb\n\n\n"),
+ ];
+ $expected = "- !bar |\n a\n b\n \n \n ";
+ $this->assertSame($expected, $this->dumper->dump($data, 2, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK));
+
+ // @todo Fix the parser, eliminate these exceptions.
+ $this->expectException(ParseException::class);
+ $this->expectExceptionMessage('Unable to parse at line 6 (near "!bar |").');
+
+ $this->parser->parse($expected, Yaml::PARSE_CUSTOM_TAGS);
}
public function testDumpingInlinedMultiLineIfRnBreakLineInTaggedValue()
@@ -528,8 +656,14 @@ public function testDumpingInlinedMultiLineIfRnBreakLineInTaggedValue()
'foo' => new TaggedValue('bar', "foo\r\nline with trailing spaces:\n \nbar\ninteger like line:\n123456789\nempty line:\n\nbaz"),
],
];
+ $expected = <<<'YAML'
+data:
+ foo: !bar "foo\r\nline with trailing spaces:\n \nbar\ninteger like line:\n123456789\nempty line:\n\nbaz"
- $this->assertSame(file_get_contents(__DIR__.'/Fixtures/multiple_lines_as_literal_block_for_tagged_values.yml'), $this->dumper->dump($data, 2, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK));
+YAML;
+ $yml = $this->dumper->dump($data, 2, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
+ $this->assertSame($expected, $yml);
+ $this->assertSameData($data, $this->parser->parse($expected, Yaml::PARSE_CUSTOM_TAGS));
}
public function testDumpMultiLineStringAsScalarBlock()
@@ -544,8 +678,27 @@ public function testDumpMultiLineStringAsScalarBlock()
],
],
];
-
- $this->assertSame(file_get_contents(__DIR__.'/Fixtures/multiple_lines_as_literal_block.yml'), $this->dumper->dump($data, 2, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK));
+ $yml = $this->dumper->dump($data, 2, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
+ $expected = str_replace("@\n", "\n", <<<'YAML'
+data:
+ single_line: 'foo bar baz'
+ multi_line: |-
+ foo
+ line with trailing spaces:
+ @
+ bar
+ integer like line:
+ 123456789
+ empty line:
+
+ baz
+ multi_line_with_carriage_return: "foo\nbar\r\nbaz"
+ nested_inlined_multi_line_string: { inlined_multi_line: "foo\nbar\r\nempty line:\n\nbaz" }
+
+YAML
+);
+ $this->assertSame($expected, $yml);
+ $this->assertSame($data, $this->parser->parse($yml));
}
public function testDumpMultiLineStringAsScalarBlockWhenFirstLineHasLeadingSpace()
@@ -558,27 +711,33 @@ public function testDumpMultiLineStringAsScalarBlockWhenFirstLineHasLeadingSpace
$expected = "data:\n multi_line: |4-\n the first line has leading spaces\n The second line does not.";
- $this->assertSame($expected, $this->dumper->dump($data, 2, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK));
+ $yml = $this->dumper->dump($data, 2, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
+ $this->assertSame($expected, $yml);
+ $this->assertSame($data, $this->parser->parse($yml));
}
public function testCarriageReturnFollowedByNewlineIsMaintainedWhenDumpingAsMultiLineLiteralBlock()
{
- $this->assertSame("- \"a\\r\\nb\\nc\"\n", $this->dumper->dump(["a\r\nb\nc"], 2, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK));
+ $data = ["a\r\nb\nc"];
+ $expected = "- \"a\\r\\nb\\nc\"\n";
+ $this->assertSame($expected, $this->dumper->dump($data, 2, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK));
+ $this->assertSame($data, $this->parser->parse($expected));
}
public function testCarriageReturnNotFollowedByNewlineIsPreservedWhenDumpingAsMultiLineLiteralBlock()
{
+ $data = [
+ 'parent' => [
+ 'foo' => "bar\n\rbaz: qux",
+ ],
+ ];
$expected = <<<'YAML'
parent:
foo: "bar\n\rbaz: qux"
YAML;
-
- $this->assertSame($expected, $this->dumper->dump([
- 'parent' => [
- 'foo' => "bar\n\rbaz: qux",
- ],
- ], 4, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK));
+ $this->assertSame($expected, $this->dumper->dump($data, 4, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK));
+ $this->assertSame($data, $this->parser->parse($expected));
}
public function testNoExtraTrailingNewlineWhenDumpingAsMultiLineLiteralBlock()
@@ -590,7 +749,15 @@ public function testNoExtraTrailingNewlineWhenDumpingAsMultiLineLiteralBlock()
$yaml = $this->dumper->dump($data, 2, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
$this->assertSame("- |-\n a\n b\n- |-\n c\n d", $yaml);
- $this->assertSame($data, Yaml::parse($yaml));
+ $this->assertSame($data, $this->parser->parse($yaml));
+ }
+
+ public function testTopLevelMultiLineStringLiteral()
+ {
+ $data = "a\nb\n";
+ $yaml = $this->dumper->dump($data, 2, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
+ $this->assertSame('"a\nb\n"', $yaml);
+ $this->assertSame($data, $this->parser->parse($yaml));
}
public function testDumpTrailingNewlineInMultiLineLiteralBlocks()
@@ -600,6 +767,7 @@ public function testDumpTrailingNewlineInMultiLineLiteralBlocks()
'clip 2' => "one\ntwo\n",
'keep 1' => "one\ntwo\n",
'keep 2' => "one\ntwo\n\n",
+ 'keep 3' => "one\ntwo\n\n\n",
'strip 1' => "one\ntwo",
'strip 2' => "one\ntwo",
];
@@ -619,6 +787,11 @@ public function testDumpTrailingNewlineInMultiLineLiteralBlocks()
one
two
+'keep 3': |+
+ one
+ two
+
+
'strip 1': |-
one
two
@@ -628,7 +801,7 @@ public function testDumpTrailingNewlineInMultiLineLiteralBlocks()
YAML;
$this->assertSame($expected, $yaml);
- $this->assertSame($data, Yaml::parse($yaml));
+ $this->assertSame($data, $this->parser->parse($yaml));
}
public function testZeroIndentationThrowsException()
@@ -664,6 +837,15 @@ public function testDumpIdeographicSpaces()
'regular_space' => 'a b',
], 2));
}
+
+ private function assertSameData($expected, $actual)
+ {
+ $this->assertEquals($expected, $actual);
+ $this->assertSame(
+ var_export($expected, true),
+ var_export($actual, true)
+ );
+ }
}
class A
diff --git a/src/Symfony/Component/Yaml/Tests/Fixtures/multiple_lines_as_literal_block.yml b/src/Symfony/Component/Yaml/Tests/Fixtures/multiple_lines_as_literal_block.yml
deleted file mode 100644
index 1f61eb1216a52..0000000000000
--- a/src/Symfony/Component/Yaml/Tests/Fixtures/multiple_lines_as_literal_block.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-data:
- single_line: 'foo bar baz'
- multi_line: |-
- foo
- line with trailing spaces:
-
- bar
- integer like line:
- 123456789
- empty line:
-
- baz
- multi_line_with_carriage_return: "foo\nbar\r\nbaz"
- nested_inlined_multi_line_string: { inlined_multi_line: "foo\nbar\r\nempty line:\n\nbaz" }
diff --git a/src/Symfony/Component/Yaml/Tests/Fixtures/multiple_lines_as_literal_block_for_tagged_values.yml b/src/Symfony/Component/Yaml/Tests/Fixtures/multiple_lines_as_literal_block_for_tagged_values.yml
deleted file mode 100644
index f8c9112fd52a5..0000000000000
--- a/src/Symfony/Component/Yaml/Tests/Fixtures/multiple_lines_as_literal_block_for_tagged_values.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-data:
- foo: !bar "foo\r\nline with trailing spaces:\n \nbar\ninteger like line:\n123456789\nempty line:\n\nbaz"
diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php
index 9a14422e434fe..6bcc937d6f49e 100644
--- a/src/Symfony/Component/Yaml/Tests/ParserTest.php
+++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php
@@ -34,6 +34,90 @@ protected function tearDown(): void
chmod(__DIR__.'/Fixtures/not_readable.yml', 0644);
}
+ public function testTopLevelNumber()
+ {
+ $yml = '5';
+ $data = $this->parser->parse($yml);
+ $expected = 5;
+ $this->assertSameData($expected, $data);
+ }
+
+ public function testTopLevelNull()
+ {
+ $yml = 'null';
+ $data = $this->parser->parse($yml);
+ $expected = null;
+ $this->assertSameData($expected, $data);
+ }
+
+ public function testTaggedValueTopLevelNumber()
+ {
+ $yml = '!number 5';
+ $data = $this->parser->parse($yml, Yaml::PARSE_CUSTOM_TAGS);
+ // @todo Preserve the number, don't turn into string.
+ $expected = new TaggedValue('number', '5');
+ $this->assertSameData($expected, $data);
+ }
+
+ public function testTaggedValueTopLevelNull()
+ {
+ $yml = '!tag null';
+ $data = $this->parser->parse($yml, Yaml::PARSE_CUSTOM_TAGS);
+ // @todo Preserve literal null, don't turn into string.
+ $expected = new TaggedValue('tag', 'null');
+ $this->assertSameData($expected, $data);
+ }
+
+ public function testTaggedValueTopLevelString()
+ {
+ $yml = '!user barbara';
+ $data = $this->parser->parse($yml, Yaml::PARSE_CUSTOM_TAGS);
+ $expected = new TaggedValue('user', 'barbara');
+ $this->assertSameData($expected, $data);
+ }
+
+ public function testTaggedValueTopLevelAssocInline()
+ {
+ $yml = '!user { name: barbara }';
+ $data = $this->parser->parse($yml, Yaml::PARSE_CUSTOM_TAGS);
+ $expected = new TaggedValue('user', ['name' => 'barbara']);
+ $this->assertSameData($expected, $data);
+ }
+
+ public function testTaggedValueTopLevelAssoc()
+ {
+ $yml = <<<'YAML'
+!user
+name: barbara
+YAML;
+ $data = $this->parser->parse($yml, Yaml::PARSE_CUSTOM_TAGS);
+ $expected = new TaggedValue('user', ['name' => 'barbara']);
+ $this->assertSameData($expected, $data);
+ }
+
+ public function testTaggedValueTopLevelList()
+ {
+ $yml = <<<'YAML'
+!users
+- barbara
+YAML;
+ $data = $this->parser->parse($yml, Yaml::PARSE_CUSTOM_TAGS);
+ $expected = new TaggedValue('users', ['barbara']);
+ $this->assertSameData($expected, $data);
+ }
+
+ public function testTaggedTextAsListItem()
+ {
+ $yml = <<<'YAML'
+- !text |
+ first line
+YAML;
+ // @todo Fix the parser, eliminate this exception.
+ $this->expectException(ParseException::class);
+ $this->expectExceptionMessage('Unable to parse at line 2 (near "!text |").');
+ $this->parser->parse($yml, Yaml::PARSE_CUSTOM_TAGS);
+ }
+
/**
* @dataProvider getDataFormSpecifications
*/
@@ -104,7 +188,7 @@ public function testParserIsStateless()
public function testValidTokenSeparation(string $given, array $expected)
{
$actual = $this->parser->parse($given);
- $this->assertEquals($expected, $actual);
+ $this->assertSameData($expected, $actual);
}
public function validTokenSeparators(): array
@@ -482,7 +566,7 @@ public function testObjectSupportEnabled()
foo: !php/object O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";}
bar: 1
EOF;
- $this->assertEquals(['foo' => new B(), 'bar' => 1], $this->parser->parse($input, Yaml::PARSE_OBJECT), '->parse() is able to parse objects');
+ $this->assertSameData(['foo' => new B(), 'bar' => 1], $this->parser->parse($input, Yaml::PARSE_OBJECT), '->parse() is able to parse objects');
}
public function testObjectSupportDisabledButNoExceptions()
@@ -491,7 +575,7 @@ public function testObjectSupportDisabledButNoExceptions()
foo: !php/object O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";}
bar: 1
EOF;
- $this->assertEquals(['foo' => null, 'bar' => 1], $this->parser->parse($input), '->parse() does not parse objects');
+ $this->assertSameData(['foo' => null, 'bar' => 1], $this->parser->parse($input), '->parse() does not parse objects');
}
/**
@@ -501,7 +585,7 @@ public function testObjectForMap($yaml, $expected)
{
$flags = Yaml::PARSE_OBJECT_FOR_MAP;
- $this->assertEquals($expected, $this->parser->parse($yaml, $flags));
+ $this->assertSameData($expected, $this->parser->parse($yaml, $flags));
}
public function getObjectForMapTests()
@@ -957,12 +1041,12 @@ public function testEmptyValue()
hash:
EOF;
- $this->assertEquals(['hash' => null], Yaml::parse($input));
+ $this->assertSame(['hash' => null], Yaml::parse($input));
}
public function testCommentAtTheRootIndent()
{
- $this->assertEquals([
+ $this->assertSame([
'services' => [
'app.foo_service' => [
'class' => 'Foo',
@@ -988,7 +1072,7 @@ class: Bar
public function testStringBlockWithComments()
{
- $this->assertEquals(['content' => <<<'EOT'
+ $this->assertSame(['content' => <<<'EOT'
# comment 1
header
@@ -1016,7 +1100,7 @@ public function testStringBlockWithComments()
public function testFoldedStringBlockWithComments()
{
- $this->assertEquals([['content' => <<<'EOT'
+ $this->assertSame([['content' => <<<'EOT'
# comment 1
header
@@ -1045,7 +1129,7 @@ public function testFoldedStringBlockWithComments()
public function testNestedFoldedStringBlockWithComments()
{
- $this->assertEquals([[
+ $this->assertSame([[
'title' => 'some title',
'content' => <<<'EOT'
# comment 1
@@ -1077,7 +1161,7 @@ public function testNestedFoldedStringBlockWithComments()
public function testReferenceResolvingInInlineStrings()
{
- $this->assertEquals([
+ $this->assertSame([
'var' => 'var-value',
'scalar' => 'var-value',
'list' => ['var-value'],
@@ -1117,7 +1201,7 @@ public function testYamlDirective()
foo: 1
bar: 2
EOF;
- $this->assertEquals(['foo' => 1, 'bar' => 2], $this->parser->parse($yaml));
+ $this->assertSame(['foo' => 1, 'bar' => 2], $this->parser->parse($yaml));
}
public function testFloatKeys()
@@ -1167,7 +1251,7 @@ public function testExplicitStringCasting()
'~' => 'null',
];
- $this->assertEquals($expected, $this->parser->parse($yaml));
+ $this->assertSame($expected, $this->parser->parse($yaml));
}
public function testColonInMappingValueException()
@@ -1468,7 +1552,7 @@ public function testParseDateAsMappingValue()
$expectedDate->setDate(2002, 12, 14);
$expectedDate->setTime(0, 0, 0);
- $this->assertEquals(['date' => $expectedDate], $this->parser->parse($yaml, Yaml::PARSE_DATETIME));
+ $this->assertSameData(['date' => $expectedDate], $this->parser->parse($yaml, Yaml::PARSE_DATETIME));
}
/**
@@ -1688,7 +1772,7 @@ public function testBackslashInSingleQuotedString()
public function testParseMultiLineString()
{
- $this->assertEquals("foo bar\nbaz", $this->parser->parse("foo\nbar\n\nbaz"));
+ $this->assertSame("foo bar\nbaz", $this->parser->parse("foo\nbar\n\nbaz"));
}
/**
@@ -1696,7 +1780,7 @@ public function testParseMultiLineString()
*/
public function testParseMultiLineMappingValue($yaml, $expected, $parseError)
{
- $this->assertEquals($expected, $this->parser->parse($yaml));
+ $this->assertSame($expected, $this->parser->parse($yaml));
}
public function multiLineDataProvider()
@@ -1763,7 +1847,7 @@ public function multiLineDataProvider()
*/
public function testInlineNotationSpanningMultipleLines($expected, string $yaml)
{
- $this->assertEquals($expected, $this->parser->parse($yaml));
+ $this->assertSame($expected, $this->parser->parse($yaml));
}
public function inlineNotationSpanningMultipleLinesProvider(): array
@@ -2137,7 +2221,7 @@ public function testRootLevelInlineMappingFollowedByMoreContentIsInvalid()
public function testTaggedInlineMapping()
{
- $this->assertEquals(new TaggedValue('foo', ['foo' => 'bar']), $this->parser->parse('!foo {foo: bar}', Yaml::PARSE_CUSTOM_TAGS));
+ $this->assertSameData(new TaggedValue('foo', ['foo' => 'bar']), $this->parser->parse('!foo {foo: bar}', Yaml::PARSE_CUSTOM_TAGS));
}
public function testInvalidInlineSequenceContainingStringWithEscapedQuotationCharacter()
@@ -2152,7 +2236,7 @@ public function testInvalidInlineSequenceContainingStringWithEscapedQuotationCha
*/
public function testCustomTagSupport($expected, $yaml)
{
- $this->assertEquals($expected, $this->parser->parse($yaml, Yaml::PARSE_CUSTOM_TAGS));
+ $this->assertSameData($expected, $this->parser->parse($yaml, Yaml::PARSE_CUSTOM_TAGS));
}
public function taggedValuesProvider()
@@ -2348,7 +2432,7 @@ public function testCanParseVeryLongValue()
$yamlString = Yaml::dump($trickyVal);
$arrayFromYaml = $this->parser->parse($yamlString);
- $this->assertEquals($trickyVal, $arrayFromYaml);
+ $this->assertSame($trickyVal, $arrayFromYaml);
}
public function testParserCleansUpReferencesBetweenRuns()
@@ -2463,7 +2547,7 @@ public function testMergeKeysWhenMappingsAreParsedAsObjects()
],
];
- $this->assertEquals($expected, $this->parser->parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP));
+ $this->assertSameData($expected, $this->parser->parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP));
}
public function testFilenamesAreParsedAsStringsWithoutFlag()
@@ -2556,7 +2640,7 @@ public function testParseReferencesOnMergeKeysWithMappingsParsedAsObjects()
],
];
- $this->assertEquals($expected, $this->parser->parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP));
+ $this->assertSameData($expected, $this->parser->parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP));
}
public function testEvalRefException()
@@ -2831,6 +2915,15 @@ public function testParseIdeographicSpaces()
'regular_space' => 'a b',
], $this->parser->parse($expected));
}
+
+ private function assertSameData($expected, $actual)
+ {
+ $this->assertEquals($expected, $actual);
+ $this->assertSame(
+ var_export($expected, true),
+ var_export($actual, true)
+ );
+ }
}
class B