From 63cd219a5c5339218c73485d9ebe9fc6ff55ec14 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Sun, 25 Nov 2018 16:28:24 +0100 Subject: [PATCH 01/22] =?UTF-8?q?[Console]=C2=A0Move=20back=20root=20excep?= =?UTF-8?q?tion=20to=20stack=20trace=20in=20verbose=20mode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Symfony/Component/Console/Application.php | 7 +++++++ .../Component/Console/Tests/ApplicationTest.php | 14 ++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index 1d415636fc3d8..680036041019b 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -783,6 +783,13 @@ protected function doRenderException(\Exception $e, OutputInterface $output) // exception related properties $trace = $e->getTrace(); + array_unshift($trace, array( + 'function' => '', + 'file' => $e->getFile() ?: 'n/a', + 'line' => $e->getLine() ?: 'n/a', + 'args' => array(), + )); + for ($i = 0, $count = \count($trace); $i < $count; ++$i) { $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : ''; $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : ''; diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index faa98e5f5fac6..7922ce273bbd6 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -776,6 +776,20 @@ public function testRenderExceptionLineBreaks() $this->assertStringMatchesFormatFile(self::$fixturesPath.'/application_renderexception_linebreaks.txt', $tester->getDisplay(true), '->renderException() keep multiple line breaks'); } + public function testRenderExceptionStackTraceContainsRootException() + { + $application = new Application(); + $application->setAutoExit(false); + $application->register('foo')->setCode(function () { + throw new \Exception('Verbose exception'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo'), array('decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); + + $this->assertContains(sprintf('() at %s:', __FILE__), $tester->getDisplay()); + } + public function testRun() { $application = new Application(); From 1c548442858d083e68fcc4a8163ef9168f14f273 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 26 Nov 2018 14:20:12 +0100 Subject: [PATCH 02/22] updated CHANGELOG for 2.8.48 --- CHANGELOG-2.8.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG-2.8.md b/CHANGELOG-2.8.md index 358b477b1467b..aa884108b6dd5 100644 --- a/CHANGELOG-2.8.md +++ b/CHANGELOG-2.8.md @@ -7,6 +7,21 @@ in 2.8 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/v2.8.0...v2.8.1 +* 2.8.48 (2018-11-26) + + * bug #28917 [DoctrineBridge] catch errors while converting to db values in data collector (alekitto) + * bug #27314 [DoctrineBridge] fix case sensitivity issue in RememberMe\DoctrineTokenProvider (PF4Public) + * bug #29308 [Translation] Use XLIFF source rather than resname when there's no target (thewilkybarkid) + * bug #26244 [BrowserKit] fixed BC Break for HTTP_HOST header (brizzz) + * bug #28147 [DomCrawler] exclude fields inside "template" tags (Gorjunov) + * bug #29271 [HttpFoundation] Fix trailing space for mime-type with parameters (Sascha Dens) + * bug #29223 [Validator] Added the missing constraints instance checks (thomasbisignani) + * bug #29182 [Form] Fixed empty data for compound date types (HeahDude) + * bug #29185 [Form] Fixed keeping hash of equal \DateTimeInterface on submit (HeahDude) + * bug #28731 [Form] invalidate forms on transformation failures (xabbuh) + * bug #29152 [Config] Unset key during normalization (ro0NL) + * bug #29057 [HttpFoundation] replace any preexisting Content-Type headers (nicolas-grekas) + * 2.8.47 (2018-11-03) * bug #29020 Fix ini_get() for boolean values (deguif) From a84a4488646ced1f468838fabad0c57bc2d1b973 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 26 Nov 2018 14:20:17 +0100 Subject: [PATCH 03/22] update CONTRIBUTORS for 2.8.48 --- CONTRIBUTORS.md | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 969c6a424cefc..1c329f2146c3e 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -14,9 +14,9 @@ Symfony is the result of the work of many people who made the code better - Victor Berchet (victor) - Robin Chalas (chalas_r) - Kévin Dunglas (dunglas) + - Maxime Steinhausser (ogizanagi) - Jakub Zalas (jakubzalas) - Johannes S (johannes) - - Maxime Steinhausser (ogizanagi) - Kris Wallsmith (kriswallsmith) - Ryan Weaver (weaverryan) - Javier Eguiluz (javier.eguiluz) @@ -37,8 +37,8 @@ Symfony is the result of the work of many people who made the code better - Benjamin Eberlei (beberlei) - Igor Wiedler (igorw) - Jules Pietri (heah) - - Eriksen Costa (eriksencosta) - Yonel Ceruto (yonelceruto) + - Eriksen Costa (eriksencosta) - Guilhem Niot (energetick) - Sarah Khalil (saro0h) - Jonathan Wage (jwage) @@ -70,6 +70,7 @@ Symfony is the result of the work of many people who made the code better - Gábor Egyed (1ed) - Mathieu Piot (mpiot) - Titouan Galopin (tgalopin) + - Vladimir Reznichenko (kalessil) - Michel Weimerskirch (mweimerskirch) - Andrej Hudec (pulzarraider) - Konstantin Myakshin (koc) @@ -77,7 +78,6 @@ Symfony is the result of the work of many people who made the code better - Jáchym Toušek (enumag) - Charles Sarrazin (csarrazi) - David Maicher (dmaicher) - - Vladimir Reznichenko (kalessil) - Christian Raue - Issei Murasawa (issei_m) - Arnout Boks (aboks) @@ -86,13 +86,13 @@ Symfony is the result of the work of many people who made the code better - Dariusz Górecki (canni) - Douglas Greenshields (shieldo) - Dariusz Ruminski + - Grégoire Paris (greg0ire) - Lee McDermott - Brandon Turner - Luis Cordova (cordoval) - Graham Campbell (graham) - Daniel Holmes (dholmes) - Toni Uebernickel (havvg) - - Grégoire Paris (greg0ire) - Bart van den Burg (burgov) - Jordan Alliot (jalliot) - Jérôme Tamarelle (gromnan) @@ -113,12 +113,12 @@ Symfony is the result of the work of many people who made the code better - lenar - Alexander Schwenn (xelaris) - Włodzimierz Gajda (gajdaw) + - Tomáš Votruba (tomas_votruba) - Peter Kokot (maastermedia) - Jacob Dreesen (jdreesen) - Florian Voutzinos (florianv) - Colin Frei - Adrien Brault (adrienbrault) - - Tomáš Votruba (tomas_votruba) - Joshua Thijssen - excelwebzone - Gordon Franke (gimler) @@ -179,6 +179,7 @@ Symfony is the result of the work of many people who made the code better - jeremyFreeAgent (Jérémy Romey) (jeremyfreeagent) - James Halsall (jaitsu) - Matthieu Napoli (mnapoli) + - Florent Mata (fmata) - Warnar Boekkooi (boekkooi) - Alessandro Chitolina (alekitto) - Dmitrii Chekaliuk (lazyhammer) @@ -193,7 +194,6 @@ Symfony is the result of the work of many people who made the code better - Dennis Benkert (denderello) - DQNEO - Benjamin Dulau (dbenjamin) - - Florent Mata (fmata) - Mathieu Lemoine (lemoinem) - Thomas Calvet (fancyweb) - Christian Schmidt @@ -258,6 +258,7 @@ Symfony is the result of the work of many people who made the code better - Benoît Burnichon (bburnichon) - Roman Marintšenko (inori) - Xavier Montaña Carreras (xmontana) + - François-Xavier de Guillebon (de-gui_f) - Mickaël Andrieu (mickaelandrieu) - Maxime Veber (nek-) - Xavier Perez @@ -294,7 +295,9 @@ Symfony is the result of the work of many people who made the code better - Thomas Lallement (raziel057) - mcfedr (mcfedr) - Colin O'Dell (colinodell) + - Fabien Bourigault (fbourigault) - Giorgio Premi + - Jan Schädlich (jschaedl) - Beau Simensen (simensen) - Michael Hirschler (mvhirsch) - Robert Kiss (kepten) @@ -305,8 +308,8 @@ Symfony is the result of the work of many people who made the code better - Jérôme Parmentier (lctrs) - Michael Babker (mbabker) - Peter Kruithof (pkruithof) - - François-Xavier de Guillebon (de-gui_f) - Michael Holm (hollo) + - Remon van de Kamp (rpkamp) - Marc Weistroff (futurecat) - Christian Schmidt - MatTheCat @@ -350,8 +353,6 @@ Symfony is the result of the work of many people who made the code better - Ricard Clau (ricardclau) - Mark Challoner (markchalloner) - Gennady Telegin (gtelegin) - - Jan Schädlich (jschaedl) - - Fabien Bourigault (fbourigault) - Ben Davies (bendavies) - Erin Millard - Artur Melo (restless) @@ -445,6 +446,7 @@ Symfony is the result of the work of many people who made the code better - lancergr - Zan Baldwin - Mihai Stancu + - Ivan Nikolaev (destillat) - Olivier Dolbeau (odolbeau) - Jan Rosier (rosier) - Alessandro Lai (jean85) @@ -461,7 +463,6 @@ Symfony is the result of the work of many people who made the code better - Boris Vujicic (boris.vujicic) - Chris Sedlmayr (catchamonkey) - Mateusz Sip (mateusz_sip) - - Remon van de Kamp - Kamil Kokot (pamil) - Seb Koelen - Christoph Mewes (xrstf) @@ -470,6 +471,7 @@ Symfony is the result of the work of many people who made the code better - Dirk Pahl (dirkaholic) - cedric lombardot (cedriclombardot) - Jonas Flodén (flojon) + - Gonzalo Vilaseca (gonzalovilaseca) - Marcin Sikoń (marphi) - Dominik Zogg (dominik.zogg) - Marek Pietrzak @@ -643,7 +645,6 @@ Symfony is the result of the work of many people who made the code better - adev - Stefan Warman - Arkadius Stefanski (arkadius) - - Gonzalo Vilaseca (gonzalovilaseca) - Tristan Maindron (tmaindron) - Wesley Lancel - Ke WANG (yktd26) @@ -665,6 +666,7 @@ Symfony is the result of the work of many people who made the code better - Sergey (upyx) - Michael Devery (mickadoo) - Antoine Corcy + - Dmitrii Poddubnyi (karser) - Sascha Grossenbacher - Szijarto Tamas - Robin Lehrmann (robinlehrmann) @@ -804,7 +806,6 @@ Symfony is the result of the work of many people who made the code better - Sofiane HADDAG (sofhad) - frost-nzcr4 - Bozhidar Hristov - - Ivan Nikolaev (destillat) - Laurent Bassin (lbassin) - andrey1s - Abhoryo @@ -989,6 +990,7 @@ Symfony is the result of the work of many people who made the code better - Mathieu Santostefano - Arjan Keeman - Máximo Cuadros (mcuadros) + - Lukas Mencl - tamirvs - julien.galenski - Christian Neff @@ -1290,6 +1292,7 @@ Symfony is the result of the work of many people who made the code better - Adrien Samson (adriensamson) - Samuel Gordalina (gordalina) - Max Romanovsky (maxromanovsky) + - Nicolas Eeckeloo (neeckeloo) - Mathieu Morlon - Daniel Tschinder - Arnaud CHASSEUX @@ -1351,6 +1354,7 @@ Symfony is the result of the work of many people who made the code better - Andrew (drew) - kor3k kor3k (kor3k) - Stelian Mocanita (stelian) + - Thomas Bisignani (toma) - Justin (wackymole) - Flavian (2much) - Gautier Deuette @@ -1448,6 +1452,7 @@ Symfony is the result of the work of many people who made the code better - Phobetor - Andreas - Markus + - Daniel Gorgan - Thomas Chmielowiec - shdev - Andrey Ryaguzov @@ -1499,6 +1504,7 @@ Symfony is the result of the work of many people who made the code better - David Barratt - Pavel.Batanov - avi123 + - Pavel Prischepa - alsar - downace - Aarón Nieves Fernández @@ -1613,6 +1619,7 @@ Symfony is the result of the work of many people who made the code better - David Zuelke - Adrian - Oleg Andreyev + - neFAST - Pierre Rineau - Maxim Lovchikov - adenkejawen @@ -1710,7 +1717,6 @@ Symfony is the result of the work of many people who made the code better - Giovanni Albero (johntree) - Jorge Martin (jorgemartind) - Joeri Verdeyen (jverdeyen) - - Dmitrii Poddubnyi (karser) - Kevin Verschaeve (keversc) - Kevin Herrera (kherge) - Luis Ramón López López (lrlopez) @@ -1872,6 +1878,7 @@ Symfony is the result of the work of many people who made the code better - zorn - Yuriy Potemkin - Emilie Lorenzo + - Edvin Hultberg - Benjamin Long - Matt Janssen - Ben Miller @@ -1992,6 +1999,7 @@ Symfony is the result of the work of many people who made the code better - Alex Carol (picard89) - Daniel Perez Pinazo (pitiflautico) - Phil Taylor (prazgod) + - Maxim Pustynnikov (pustynnikov) - Brayden Williams (redstar504) - Rich Sage (richsage) - Rokas Mikalkėnas (rokasm) From a1a21f97a2189944b3b1907ac8b733df01a4132a Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 26 Nov 2018 14:20:43 +0100 Subject: [PATCH 04/22] updated VERSION for 2.8.48 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index e91c1d7d4712a..b35ddef8b1cc4 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -59,12 +59,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.8.48-DEV'; + const VERSION = '2.8.48'; const VERSION_ID = 20848; const MAJOR_VERSION = 2; const MINOR_VERSION = 8; const RELEASE_VERSION = 48; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '11/2018'; const END_OF_LIFE = '11/2019'; From 70e05c643fe1a982e412a3e4ac0b3b35283ffba2 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 26 Nov 2018 15:04:35 +0100 Subject: [PATCH 05/22] updated CHANGELOG for 3.4.19 --- CHANGELOG-3.4.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/CHANGELOG-3.4.md b/CHANGELOG-3.4.md index 1f2a2de81d321..dcd1c3bf06293 100644 --- a/CHANGELOG-3.4.md +++ b/CHANGELOG-3.4.md @@ -7,6 +7,44 @@ in 3.4 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v3.4.0...v3.4.1 +* 3.4.19 (2018-11-26) + + * bug #29318 [Console] Move back root exception to stack trace in verbose mode (chalasr) + * bug #29332 [PropertyAccess] make cache keys encoding bijective (nicolas-grekas) + * bug #29297 [Routing] fix trailing slash redirection when using RedirectableUrlMatcher (nicolas-grekas) + * bug #29313 [PropertyAccessor] fix encoding of cache keys (nicolas-grekas) + * bug #28917 [DoctrineBridge] catch errors while converting to db values in data collector (alekitto) + * bug #29317 [WebProfiler] Detect non-file paths in file viewer (ro0NL) + * bug #29305 [EventDispatcher] Unwrap wrapped listeners internally (ro0NL) + * bug #27314 [DoctrineBridge] fix case sensitivity issue in RememberMe\DoctrineTokenProvider (PF4Public) + * bug #29308 [Translation] Use XLIFF source rather than resname when there's no target (thewilkybarkid) + * bug #26244 [BrowserKit] fixed BC Break for HTTP_HOST header (brizzz) + * bug #28147 [DomCrawler] exclude fields inside "template" tags (Gorjunov) + * bug #29222 [Dotenv] properly parse backslashes in unquoted env vars (xabbuh) + * bug #29256 [HttpFoundation] Fixed absolute Request URI with default port (thomasbisignani) + * bug #29274 [Routing] Remove duplicate schemes and methods for invokable controllers (claudusd) + * bug #29271 [HttpFoundation] Fix trailing space for mime-type with parameters (Sascha Dens) + * bug #29243 [Cache] fix optimizing Psr6Cache for AdapterInterface pools (nicolas-grekas) + * bug #29247 [DI] fix taking lazy services into account when dumping the container (nicolas-grekas) + * bug #29249 [Form] Fixed empty data for compound date interval (HeahDude) + * bug #29265 [Bridge/PhpUnit] Use composer to download phpunit (nicolas-grekas) + * bug #28769 [FrameworkBundle] deal with explicitly enabled workflow nodes (xabbuh) + * bug #29223 [Validator] Added the missing constraints instance checks (thomasbisignani) + * bug #28966 [PropertyAccessor] Fix unable to write to singular property using setter while plural adder/remover exist (karser) + * bug #29182 [Form] Fixed empty data for compound date types (HeahDude) + * bug #29185 [Form] Fixed keeping hash of equal \DateTimeInterface on submit (HeahDude) + * bug #29137 [Workflow][FrameworkBundle] fixed guard event names for transitions (destillat, lyrixx) + * bug #28731 [Form] invalidate forms on transformation failures (xabbuh) + * bug #29152 [Config] Unset key during normalization (ro0NL) + * bug #29165 [DI] align IniFileLoader to PHP bugfix #76965 (nicolas-grekas) + * bug #29115 Change button_widget class to btn-primary (neFAST) + * bug #29131 [Dotenv] dont use getenv() to read SYMFONY_DOTENV_VARS (nicolas-grekas) + * bug #29057 [HttpFoundation] replace any preexisting Content-Type headers (nicolas-grekas) + * bug #29104 [DI] fix dumping inlined services (nicolas-grekas) + * bug #29054 [VarDumper] fix dump of closures created from callables (nicolas-grekas) + * bug #29102 [DI] fix GraphvizDumper ignoring inline definitions (nicolas-grekas) + * bug #29107 [DI] dont track classes/interfaces used to compute autowiring error messages (nicolas-grekas) + * 3.4.18 (2018-11-03) * bug #28820 [DependencyInjection] Fix tags on multiple decorated service (Soner Sayakci) From ecb09280ef60a513e9955351fd601675a9fc3223 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 26 Nov 2018 15:04:48 +0100 Subject: [PATCH 06/22] updated VERSION for 3.4.19 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 6b80158cf2407..bfc961f50511f 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -67,12 +67,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private $requestStackSize = 0; private $resetServices = false; - const VERSION = '3.4.19-DEV'; + const VERSION = '3.4.19'; const VERSION_ID = 30419; const MAJOR_VERSION = 3; const MINOR_VERSION = 4; const RELEASE_VERSION = 19; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '11/2020'; const END_OF_LIFE = '11/2021'; From acce087074962ea214f0384546d7c720432153cd Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 26 Nov 2018 15:50:31 +0100 Subject: [PATCH 07/22] bumped Symfony version to 3.4.20 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index bfc961f50511f..375e26ee0f5e1 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -67,12 +67,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private $requestStackSize = 0; private $resetServices = false; - const VERSION = '3.4.19'; - const VERSION_ID = 30419; + const VERSION = '3.4.20-DEV'; + const VERSION_ID = 30420; const MAJOR_VERSION = 3; const MINOR_VERSION = 4; - const RELEASE_VERSION = 19; - const EXTRA_VERSION = ''; + const RELEASE_VERSION = 20; + const EXTRA_VERSION = 'DEV'; const END_OF_MAINTENANCE = '11/2020'; const END_OF_LIFE = '11/2021'; From 43cb7cfb23a8d3920ad2e118fd8ce6d521dfee49 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 26 Nov 2018 15:52:06 +0100 Subject: [PATCH 08/22] updated CHANGELOG for 4.1.8 --- CHANGELOG-4.1.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/CHANGELOG-4.1.md b/CHANGELOG-4.1.md index 60b848a532545..ad6a91cdd0b1b 100644 --- a/CHANGELOG-4.1.md +++ b/CHANGELOG-4.1.md @@ -7,6 +7,49 @@ in 4.1 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.1.0...v4.1.1 +* 4.1.8 (2018-11-26) + + * bug #29318 [Console] Move back root exception to stack trace in verbose mode (chalasr) + * bug #29332 [PropertyAccess] make cache keys encoding bijective (nicolas-grekas) + * bug #29298 [Routing] fix trailing slash redirection when using RedirectableUrlMatcher (nicolas-grekas) + * bug #29297 [Routing] fix trailing slash redirection when using RedirectableUrlMatcher (nicolas-grekas) + * bug #29313 [PropertyAccessor] fix encoding of cache keys (nicolas-grekas) + * bug #28917 [DoctrineBridge] catch errors while converting to db values in data collector (alekitto) + * bug #29317 [WebProfiler] Detect non-file paths in file viewer (ro0NL) + * bug #29305 [EventDispatcher] Unwrap wrapped listeners internally (ro0NL) + * bug #27314 [DoctrineBridge] fix case sensitivity issue in RememberMe\DoctrineTokenProvider (PF4Public) + * bug #29310 [MonologBridge] Return empty list for unknown requests (ro0NL) + * bug #29308 [Translation] Use XLIFF source rather than resname when there's no target (thewilkybarkid) + * bug #26244 [BrowserKit] fixed BC Break for HTTP_HOST header (brizzz) + * bug #28147 [DomCrawler] exclude fields inside "template" tags (Gorjunov) + * bug #29222 [Dotenv] properly parse backslashes in unquoted env vars (xabbuh) + * bug #29256 [HttpFoundation] Fixed absolute Request URI with default port (thomasbisignani) + * bug #29274 [Routing] Remove duplicate schemes and methods for invokable controllers (claudusd) + * bug #29271 [HttpFoundation] Fix trailing space for mime-type with parameters (Sascha Dens) + * bug #29243 [Cache] fix optimizing Psr6Cache for AdapterInterface pools (nicolas-grekas) + * bug #29247 [DI] fix taking lazy services into account when dumping the container (nicolas-grekas) + * bug #29249 [Form] Fixed empty data for compound date interval (HeahDude) + * bug #29265 [Bridge/PhpUnit] Use composer to download phpunit (nicolas-grekas) + * bug #28769 [FrameworkBundle] deal with explicitly enabled workflow nodes (xabbuh) + * bug #29223 [Validator] Added the missing constraints instance checks (thomasbisignani) + * bug #28966 [PropertyAccessor] Fix unable to write to singular property using setter while plural adder/remover exist (karser) + * bug #29182 [Form] Fixed empty data for compound date types (HeahDude) + * bug #29191 [Routing] generate(null) should throw an exception (nicolas-grekas) + * bug #29185 [Form] Fixed keeping hash of equal \DateTimeInterface on submit (HeahDude) + * bug #29141 [Workflow] Fixed bug of buildTransitionBlockerList when many transition are enabled (Tetragramat, lyrixx) + * bug #29137 [Workflow][FrameworkBundle] fixed guard event names for transitions (destillat, lyrixx) + * bug #28731 [Form] invalidate forms on transformation failures (xabbuh) + * bug #29152 [Config] Unset key during normalization (ro0NL) + * bug #29165 [DI] align IniFileLoader to PHP bugfix #76965 (nicolas-grekas) + * bug #29115 Change button_widget class to btn-primary (neFAST) + * bug #29131 [Dotenv] dont use getenv() to read SYMFONY_DOTENV_VARS (nicolas-grekas) + * bug #29057 [HttpFoundation] replace any preexisting Content-Type headers (nicolas-grekas) + * bug #29076 [Serializer] Allow null values when denormalizing with constructor missing data (danut007ro) + * bug #29104 [DI] fix dumping inlined services (nicolas-grekas) + * bug #29054 [VarDumper] fix dump of closures created from callables (nicolas-grekas) + * bug #29102 [DI] fix GraphvizDumper ignoring inline definitions (nicolas-grekas) + * bug #29107 [DI] dont track classes/interfaces used to compute autowiring error messages (nicolas-grekas) + * 4.1.7 (2018-11-03) * bug #28820 [DependencyInjection] Fix tags on multiple decorated service (Soner Sayakci) From 63201c518c6baacd884f70961377e5c4311e5669 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 26 Nov 2018 15:52:15 +0100 Subject: [PATCH 09/22] updated VERSION for 4.1.8 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index f5042995320d7..244f664a6454c 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -63,12 +63,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private $requestStackSize = 0; private $resetServices = false; - const VERSION = '4.1.8-DEV'; + const VERSION = '4.1.8'; const VERSION_ID = 40108; const MAJOR_VERSION = 4; const MINOR_VERSION = 1; const RELEASE_VERSION = 8; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '01/2019'; const END_OF_LIFE = '07/2019'; From 9fba843a2a3f6c543051bad5d66d632808d6c5b8 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 26 Nov 2018 16:00:55 +0100 Subject: [PATCH 10/22] bumped Symfony version to 4.1.9 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 244f664a6454c..0a3909dff51c1 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -63,12 +63,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private $requestStackSize = 0; private $resetServices = false; - const VERSION = '4.1.8'; - const VERSION_ID = 40108; + const VERSION = '4.1.9-DEV'; + const VERSION_ID = 40109; const MAJOR_VERSION = 4; const MINOR_VERSION = 1; - const RELEASE_VERSION = 8; - const EXTRA_VERSION = ''; + const RELEASE_VERSION = 9; + const EXTRA_VERSION = 'DEV'; const END_OF_MAINTENANCE = '01/2019'; const END_OF_LIFE = '07/2019'; From 45c3de044e5fb31d74d8ac1899edf8a74a1da08a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Delprat?= Date: Tue, 27 Nov 2018 13:52:28 +0900 Subject: [PATCH 11/22] Fixes sprintf(): Too few arguments in Translator Fixes the log produced when the method is called : Before : "sprintf(): Too few arguments" After : "The "Symfony\Component\Translation\Translator::transChoice()" method is deprecated since Symfony 4.2, use the trans() one instead with a "%count%" parameter." Reference : http://php.net/manual/function.sprintf.php --- src/Symfony/Component/Translation/Translator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Translation/Translator.php b/src/Symfony/Component/Translation/Translator.php index 864db835c77a3..7fcb77853d5e1 100644 --- a/src/Symfony/Component/Translation/Translator.php +++ b/src/Symfony/Component/Translation/Translator.php @@ -226,7 +226,7 @@ public function trans($id, array $parameters = array(), $domain = null, $locale */ public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) { - @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2, use the trans() one instead with a "%count%" parameter.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2, use the trans() one instead with a "%%count%%" parameter.', __METHOD__), E_USER_DEPRECATED); if (!$this->formatter instanceof ChoiceMessageFormatterInterface) { throw new LogicException(sprintf('The formatter "%s" does not support plural translations.', \get_class($this->formatter))); From 53f28bf34ceec93a3a2751d2217a8691d8116970 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 27 Nov 2018 08:29:59 +0100 Subject: [PATCH 12/22] Update PR template --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index b6f39741d9dbc..ac82d7063d015 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,6 @@ | Q | A | ------------- | --- -| Branch? | master for features / 2.8 up to 4.1 for bug fixes +| Branch? | master for features / 3.4 up to 4.2 for bug fixes | Bug fix? | yes/no | New feature? | yes/no | BC breaks? | no From e2b4c8d3c9cc5d2b1b152f99b2a8b8904948ae35 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 27 Nov 2018 11:58:55 +0100 Subject: [PATCH 13/22] [Debug] workaround opcache bug mutating "$this" !?! --- phpunit | 2 +- src/Symfony/Component/Debug/DebugClassLoader.php | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/phpunit b/phpunit index f4b80ed064121..9975195309a81 100755 --- a/phpunit +++ b/phpunit @@ -1,7 +1,7 @@ #!/usr/bin/env php isFinder && !isset($this->loaded[$class])) { $this->loaded[$class] = true; - if ($file = $this->classLoader[0]->findFile($class) ?: false) { - $wasCached = \function_exists('opcache_is_script_cached') && @opcache_is_script_cached($file); - + if (!$file = $this->classLoader[0]->findFile($class) ?: false) { + // no-op + } elseif (\function_exists('opcache_is_script_cached') && @opcache_is_script_cached($file)) { require $file; - if ($wasCached) { - return; - } + return; + } else { + require $file; } } else { \call_user_func($this->classLoader, $class); From d903dcbac565bde4598b29dc445e74401c533f88 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Tue, 27 Nov 2018 11:41:07 +1300 Subject: [PATCH 14/22] [Form] Handle all case variants of "nan" when parsing a number Fixes #29321 --- .../Core/DataTransformer/NumberToLocalizedStringTransformer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php index 9e03606a3ec70..3e125bc82be36 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php @@ -146,7 +146,7 @@ public function reverseTransform($value) return; } - if ('NaN' === $value) { + if (\in_array($value, array('NaN', 'NAN', 'nan'), true)) { throw new TransformationFailedException('"NaN" is not a valid number'); } From 85af682834a56ff0b7775d4dd1078b748db0cf52 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 28 Nov 2018 11:07:27 +0100 Subject: [PATCH 15/22] add a test case --- .../NumberToLocalizedStringTransformerTest.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php index d6a4662102232..176d3a9a58f9e 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php @@ -514,24 +514,24 @@ public function testReverseTransformExpectsValidNumber() /** * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + * @dataProvider nanRepresentationProvider * * @see https://github.com/symfony/symfony/issues/3161 */ - public function testReverseTransformDisallowsNaN() + public function testReverseTransformDisallowsNaN($nan) { $transformer = new NumberToLocalizedStringTransformer(); - $transformer->reverseTransform('NaN'); + $transformer->reverseTransform($nan); } - /** - * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException - */ - public function testReverseTransformDisallowsNaN2() + public function nanRepresentationProvider() { - $transformer = new NumberToLocalizedStringTransformer(); - - $transformer->reverseTransform('nan'); + return array( + array('nan'), + array('NaN'), // see https://github.com/symfony/symfony/issues/3161 + array('NAN'), + ); } /** From 3cd4477563f6f56760c03a6864e38f5328534c19 Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Wed, 28 Nov 2018 16:00:13 +0100 Subject: [PATCH 16/22] [Messenger] Mention HandleTrait in UPGRADE-4.2.md file --- UPGRADE-4.2.md | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/UPGRADE-4.2.md b/UPGRADE-4.2.md index bdf2ab0cececf..a2fadabc95b14 100644 --- a/UPGRADE-4.2.md +++ b/UPGRADE-4.2.md @@ -199,7 +199,10 @@ Messenger --------- * The `MiddlewareInterface::handle()` and `SenderInterface::send()` methods must now return an `Envelope` instance. - * The return value of handlers is ignored. If you used to return a value, e.g in query bus handlers, you can either: + * The return value of handlers isn't forwarded anymore by middleware and buses. + If you used to return a value, e.g in query bus handlers, you can either: + - get the result from the `HandledStamp` in the envelope returned by the bus. + - use the `HandleTrait` to leverage a message bus, expecting a single, synchronous message handling and returning its result. - make your `Query` mutable to allow setting & getting a result: ```php // When dispatching: @@ -209,15 +212,6 @@ Messenger // In your handler: $query->setResult($yourResult); ``` - - define a callable on your `Query` to be called in your handler: - ```php - // When dispatching: - $bus->dispatch(new Query([$this, 'onResult'])); - - // In your handler: - $query->executeCallback($yourResult); - ``` - * The `EnvelopeAwareInterface` was removed and the `MiddlewareInterface::handle()` method now requires an `Envelope` object as first argument. When using built-in middleware with the provided `MessageBus`, you will not have to do anything. If you use your own `MessageBusInterface` implementation, you must wrap the message in an `Envelope` before passing it to middleware. From 0d0be12e07b1e04e4d87286dbeb1c64302dccbfa Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 27 Nov 2018 11:55:51 +0100 Subject: [PATCH 17/22] [DI] fix combinatorial explosion when analyzing the service graph --- .../Compiler/InlineServiceDefinitionsPass.php | 12 +++- .../DependencyInjection/Dumper/PhpDumper.php | 66 +++++++++++++------ 2 files changed, 57 insertions(+), 21 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php index 05eb72d9746f4..65da0b9ac7ecb 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php @@ -127,13 +127,19 @@ private function isInlineableDefinition($id, Definition $definition, ServiceRefe } $ids = array(); + $isReferencedByConstructor = false; foreach ($graph->getNode($id)->getInEdges() as $edge) { + $isReferencedByConstructor = $isReferencedByConstructor || $edge->isReferencedByConstructor(); if ($edge->isWeak()) { return false; } $ids[] = $edge->getSourceNode()->getId(); } + if (!$ids) { + return true; + } + if (\count(array_unique($ids)) > 1) { return false; } @@ -142,6 +148,10 @@ private function isInlineableDefinition($id, Definition $definition, ServiceRefe return false; } - return !$ids || $this->container->getDefinition($ids[0])->isShared(); + if ($isReferencedByConstructor && $this->container->getDefinition($ids[0])->isLazy() && ($definition->getProperties() || $definition->getMethodCalls() || $definition->getConfigurator())) { + return false; + } + + return $this->container->getDefinition($ids[0])->isShared(); } } diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index fd7eec05759b7..95a98c6aaacae 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -155,17 +155,18 @@ public function dump(array $options = array()) } (new AnalyzeServiceReferencesPass(false, !$this->getProxyDumper() instanceof NullDumper))->process($this->container); + $checkedNodes = array(); $this->circularReferences = array(); - foreach (array(true, false) as $byConstructor) { - foreach ($this->container->getCompiler()->getServiceReferenceGraph()->getNodes() as $id => $node) { - if (!$node->getValue() instanceof Definition) { - continue; - } - $currentPath = array($id => true); - $this->analyzeCircularReferences($node->getOutEdges(), $currentPath, $id, $byConstructor); + foreach ($this->container->getCompiler()->getServiceReferenceGraph()->getNodes() as $id => $node) { + if (!$node->getValue() instanceof Definition) { + continue; + } + if (!isset($checkedNodes[$id])) { + $this->analyzeCircularReferences($id, $node->getOutEdges(), $checkedNodes); } } $this->container->getCompiler()->getServiceReferenceGraph()->clear(); + $checkedNodes = array(); $this->docStar = $options['debug'] ? '*' : ''; @@ -301,12 +302,12 @@ private function getProxyDumper() return $this->proxyDumper; } - private function analyzeCircularReferences(array $edges, &$currentPath, $sourceId, $byConstructor) + private function analyzeCircularReferences($sourceId, array $edges, &$checkedNodes, &$currentPath = array()) { + $checkedNodes[$sourceId] = true; + $currentPath[$sourceId] = $sourceId; + foreach ($edges as $edge) { - if ($byConstructor && !$edge->isReferencedByConstructor()) { - continue; - } $node = $edge->getDestNode(); $id = $node->getId(); @@ -315,20 +316,42 @@ private function analyzeCircularReferences(array $edges, &$currentPath, $sourceI } elseif (isset($currentPath[$id])) { $currentId = $id; foreach (array_reverse($currentPath) as $parentId) { - if (!isset($this->circularReferences[$parentId][$currentId])) { - $this->circularReferences[$parentId][$currentId] = $byConstructor; + $this->circularReferences[$parentId][$currentId] = $currentId; + if ($parentId === $id) { + break; } + $currentId = $parentId; + } + } elseif (!isset($checkedNodes[$id])) { + $this->analyzeCircularReferences($id, $node->getOutEdges(), $checkedNodes, $currentPath); + } elseif (isset($this->circularReferences[$id])) { + $this->connectCircularReferences($id, $currentPath); + } + } + unset($currentPath[$sourceId]); + } + + private function connectCircularReferences($sourceId, &$currentPath, &$subPath = array()) + { + $subPath[$sourceId] = $sourceId; + $currentPath[$sourceId] = $sourceId; + + foreach ($this->circularReferences[$sourceId] as $id) { + if (isset($currentPath[$id])) { + $currentId = $id; + foreach (array_reverse($currentPath) as $parentId) { + $this->circularReferences[$parentId][$currentId] = $currentId; if ($parentId === $id) { break; } $currentId = $parentId; } - } else { - $currentPath[$id] = $id; - $this->analyzeCircularReferences($node->getOutEdges(), $currentPath, $id, $byConstructor); - unset($currentPath[$id]); + } elseif (!isset($subPath[$id]) && isset($this->circularReferences[$id])) { + $this->connectCircularReferences($id, $currentPath, $subPath); } } + unset($currentPath[$sourceId]); + unset($subPath[$sourceId]); } private function collectLineage($class, array &$lineage) @@ -569,8 +592,11 @@ private function addServiceConfigurator(Definition $definition, $variableName = if (\is_array($callable)) { if ($callable[0] instanceof Reference - || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))) { - return sprintf(" %s->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); + || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0])) + ) { + $callable[0] = $this->dumpValue($callable[0]); + + return sprintf(' '.('$' === $callable[0][0] ? '%s' : '(%s)')."->%s(\$%s);\n", $callable[0], $callable[1], $variableName); } $class = $this->dumpValue($callable[0]); @@ -724,7 +750,7 @@ private function addInlineReference($id, Definition $definition, $targetId, $for $hasSelfRef = isset($this->circularReferences[$id][$targetId]); $forConstructor = $forConstructor && !isset($this->definitionVariables[$definition]); - $code = $hasSelfRef && $this->circularReferences[$id][$targetId] && !$forConstructor ? $this->addInlineService($id, $definition, $definition) : ''; + $code = $hasSelfRef && !$forConstructor ? $this->addInlineService($id, $definition, $definition) : ''; if (isset($this->referenceVariables[$targetId]) || (2 > $callCount && (!$hasSelfRef || !$forConstructor))) { return $code; From 977a007e3ac99224564dfeead8ec4d32270bc885 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Thu, 29 Nov 2018 09:33:43 +0100 Subject: [PATCH 18/22] typo --- .../Component/Validator/Context/ExecutionContextInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php b/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php index 544c82f6c074b..e4f7df1757da6 100644 --- a/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php +++ b/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php @@ -112,7 +112,7 @@ public function getValidator(); * Returns the currently validated object. * * If the validator is currently validating a class constraint, the - * object of that class is returned. If it is a validating a property or + * object of that class is returned. If it is validating a property or * getter constraint, the object that the property/getter belongs to is * returned. * From fa234378ffa9a361dbf429cb4d6464d8a6db1f35 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 27 Nov 2018 11:31:37 +0100 Subject: [PATCH 19/22] calculate cache keys for property setters depending on the value --- .../PropertyAccess/PropertyAccessor.php | 51 ++++++++++++------- .../Tests/PropertyAccessorTest.php | 24 ++++++++- .../Tests/TestPluralAdderRemoverAndSetter.php | 37 ++++++++++++++ ...rRemoverAndSetterSameSingularAndPlural.php | 28 ++++++++++ 4 files changed, 121 insertions(+), 19 deletions(-) create mode 100644 src/Symfony/Component/PropertyAccess/Tests/TestPluralAdderRemoverAndSetter.php create mode 100644 src/Symfony/Component/PropertyAccess/Tests/TestPluralAdderRemoverAndSetterSameSingularAndPlural.php diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 90e4c234fb1dd..2e358f458600b 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -687,7 +687,8 @@ private function writeCollection($zval, $property, $collection, $addMethod, $rem */ private function getWriteAccessInfo($class, $property, $value) { - $key = str_replace('\\', '.', $class).'..'.$property; + $useAdderAndRemover = \is_array($value) || $value instanceof \Traversable; + $key = str_replace('\\', '.', $class).'..'.$property.'..'.(int) $useAdderAndRemover; if (isset($this->writePropertyCache[$key])) { return $this->writePropertyCache[$key]; @@ -707,6 +708,16 @@ private function getWriteAccessInfo($class, $property, $value) $camelized = $this->camelize($property); $singulars = (array) Inflector::singularize($camelized); + if ($useAdderAndRemover) { + $methods = $this->findAdderAndRemover($reflClass, $singulars); + + if (null !== $methods) { + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_ADDER_AND_REMOVER; + $access[self::ACCESS_ADDER] = $methods[0]; + $access[self::ACCESS_REMOVER] = $methods[1]; + } + } + if (!isset($access[self::ACCESS_TYPE])) { $setter = 'set'.$camelized; $getsetter = lcfirst($camelized); // jQuery style, e.g. read: last(), write: last($item) @@ -728,22 +739,16 @@ private function getWriteAccessInfo($class, $property, $value) $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_MAGIC; $access[self::ACCESS_NAME] = $setter; } elseif (null !== $methods = $this->findAdderAndRemover($reflClass, $singulars)) { - if (\is_array($value) || $value instanceof \Traversable) { - $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_ADDER_AND_REMOVER; - $access[self::ACCESS_ADDER] = $methods[0]; - $access[self::ACCESS_REMOVER] = $methods[1]; - } else { - $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_NOT_FOUND; - $access[self::ACCESS_NAME] = sprintf( - 'The property "%s" in class "%s" can be defined with the methods "%s()" but '. - 'the new value must be an array or an instance of \Traversable, '. - '"%s" given.', - $property, - $reflClass->name, - implode('()", "', $methods), - \is_object($value) ? \get_class($value) : \gettype($value) - ); - } + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_NOT_FOUND; + $access[self::ACCESS_NAME] = sprintf( + 'The property "%s" in class "%s" can be defined with the methods "%s()" but '. + 'the new value must be an array or an instance of \Traversable, '. + '"%s" given.', + $property, + $reflClass->name, + implode('()", "', $methods), + \is_object($value) ? \get_class($value) : \gettype($value) + ); } else { $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_NOT_FOUND; $access[self::ACCESS_NAME] = sprintf( @@ -783,6 +788,18 @@ private function isPropertyWritable($object, $property) $access = $this->getWriteAccessInfo(\get_class($object), $property, array()); + $isWritable = self::ACCESS_TYPE_METHOD === $access[self::ACCESS_TYPE] + || self::ACCESS_TYPE_PROPERTY === $access[self::ACCESS_TYPE] + || self::ACCESS_TYPE_ADDER_AND_REMOVER === $access[self::ACCESS_TYPE] + || (!$access[self::ACCESS_HAS_PROPERTY] && property_exists($object, $property)) + || self::ACCESS_TYPE_MAGIC === $access[self::ACCESS_TYPE]; + + if ($isWritable) { + return true; + } + + $access = $this->getWriteAccessInfo(\get_class($object), $property, ''); + return self::ACCESS_TYPE_METHOD === $access[self::ACCESS_TYPE] || self::ACCESS_TYPE_PROPERTY === $access[self::ACCESS_TYPE] || self::ACCESS_TYPE_ADDER_AND_REMOVER === $access[self::ACCESS_TYPE] diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php index cf6152380d1f2..894fbd5c0df0e 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -719,7 +719,27 @@ public function testWriteToPluralPropertyWhileSingularOneExists() $this->propertyAccessor->isWritable($object, 'emails'); //cache access info $this->propertyAccessor->setValue($object, 'emails', array('test@email.com')); - self::assertEquals(array('test@email.com'), $object->getEmails()); - self::assertNull($object->getEmail()); + $this->assertEquals(array('test@email.com'), $object->getEmails()); + $this->assertNull($object->getEmail()); + } + + public function testAdderAndRemoverArePreferredOverSetter() + { + $object = new TestPluralAdderRemoverAndSetter(); + + $this->propertyAccessor->isWritable($object, 'emails'); //cache access info + $this->propertyAccessor->setValue($object, 'emails', array('test@email.com')); + + $this->assertEquals(array('test@email.com'), $object->getEmails()); + } + + public function testAdderAndRemoverArePreferredOverSetterForSameSingularAndPlural() + { + $object = new TestPluralAdderRemoverAndSetterSameSingularAndPlural(); + + $this->propertyAccessor->isWritable($object, 'aircraft'); //cache access info + $this->propertyAccessor->setValue($object, 'aircraft', array('aeroplane')); + + $this->assertEquals(array('aeroplane'), $object->getAircraft()); } } diff --git a/src/Symfony/Component/PropertyAccess/Tests/TestPluralAdderRemoverAndSetter.php b/src/Symfony/Component/PropertyAccess/Tests/TestPluralAdderRemoverAndSetter.php new file mode 100644 index 0000000000000..ecb3f9b4a9d32 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Tests/TestPluralAdderRemoverAndSetter.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Tests; + +class TestPluralAdderRemoverAndSetter +{ + private $emails = array(); + + public function getEmails() + { + return $this->emails; + } + + public function setEmails(array $emails) + { + $this->emails = array('foo@email.com'); + } + + public function addEmail($email) + { + $this->emails[] = $email; + } + + public function removeEmail($email) + { + $this->emails = array_diff($this->emails, array($email)); + } +} diff --git a/src/Symfony/Component/PropertyAccess/Tests/TestPluralAdderRemoverAndSetterSameSingularAndPlural.php b/src/Symfony/Component/PropertyAccess/Tests/TestPluralAdderRemoverAndSetterSameSingularAndPlural.php new file mode 100644 index 0000000000000..bb3b4f4688dc5 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Tests/TestPluralAdderRemoverAndSetterSameSingularAndPlural.php @@ -0,0 +1,28 @@ +aircraft; + } + + public function setAircraft(array $aircraft) + { + $this->aircraft = array('plane'); + } + + public function addAircraft($aircraft) + { + $this->aircraft[] = $aircraft; + } + + public function removeAircraft($aircraft) + { + $this->aircraft = array_diff($this->aircraft, array($aircraft)); + } +} From fbaba23023fa1e13005120c41306dea71abcfb16 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 29 Nov 2018 12:01:52 +0100 Subject: [PATCH 20/22] [Routing] fix trailing slash redirection --- .../Matcher/Dumper/PhpMatcherDumper.php | 10 ++++++++-- .../Component/Routing/Matcher/UrlMatcher.php | 9 +++++++-- .../Tests/Fixtures/dumper/url_matcher1.php | 18 ++++++++++++++---- .../Tests/Fixtures/dumper/url_matcher10.php | 9 +++++++-- .../Tests/Fixtures/dumper/url_matcher11.php | 9 +++++++-- .../Tests/Fixtures/dumper/url_matcher12.php | 9 +++++++-- .../Tests/Fixtures/dumper/url_matcher2.php | 18 ++++++++++++++---- .../Tests/Fixtures/dumper/url_matcher3.php | 18 ++++++++++++++---- .../Tests/Fixtures/dumper/url_matcher4.php | 9 +++++++-- .../Tests/Fixtures/dumper/url_matcher5.php | 18 ++++++++++++++---- .../Tests/Fixtures/dumper/url_matcher6.php | 18 ++++++++++++++---- .../Tests/Fixtures/dumper/url_matcher7.php | 18 ++++++++++++++---- .../Tests/Fixtures/dumper/url_matcher8.php | 9 +++++++-- .../Routing/Tests/Matcher/UrlMatcherTest.php | 9 +++++++++ 14 files changed, 143 insertions(+), 38 deletions(-) diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php index 4cb1c7429bb10..c33b7d06c56f4 100644 --- a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php +++ b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php @@ -550,9 +550,15 @@ private function compileStaticPrefixCollection(StaticPrefixCollection $tree, \st private function compileSwitchDefault(bool $hasVars, bool $matchHost): string { $code = sprintf(" - if ('/' !== \$pathinfo && \$hasTrailingSlash !== ('/' === \$pathinfo[-1])) { - %s; + if ('/' !== \$pathinfo) { + if (!\$hasTrailingSlash && '/' === \$pathinfo[-1]%s) { + %s; + } + if (\$hasTrailingSlash && '/' !== \$pathinfo[-1]) { + %2\$s; + } }\n", + $hasVars ? ' && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n[\'MARK\']' : '', $this->supportsRedirections ? 'return null' : 'break' ); diff --git a/src/Symfony/Component/Routing/Matcher/UrlMatcher.php b/src/Symfony/Component/Routing/Matcher/UrlMatcher.php index f2b353dfeb140..13b231bf96fdb 100644 --- a/src/Symfony/Component/Routing/Matcher/UrlMatcher.php +++ b/src/Symfony/Component/Routing/Matcher/UrlMatcher.php @@ -160,8 +160,13 @@ protected function matchCollection($pathinfo, RouteCollection $routes) continue; } - if ($supportsTrailingSlash && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - return; + if ($supportsTrailingSlash) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1))) { + return; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + return; + } } $hostMatches = array(); diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php index 531d1ecabae75..472b96a35cdc7 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php @@ -54,8 +54,13 @@ public function match($rawPathinfo) } list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - break; + if ('/' !== $pathinfo) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1]) { + break; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + break; + } } if ($requiredHost) { @@ -232,8 +237,13 @@ public function match($rawPathinfo) list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - break; + if ('/' !== $pathinfo) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { + break; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + break; + } } foreach ($vars as $i => $v) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher10.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher10.php index eee7e080d89fd..956a8bad689d3 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher10.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher10.php @@ -2793,8 +2793,13 @@ public function match($rawPathinfo) list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - break; + if ('/' !== $pathinfo) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { + break; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + break; + } } foreach ($vars as $i => $v) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher11.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher11.php index 8f3f0599f2eb4..3cc68ed9931fb 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher11.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher11.php @@ -118,8 +118,13 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - return null; + if ('/' !== $pathinfo) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { + return null; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + return null; + } } foreach ($vars as $i => $v) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher12.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher12.php index 406e13336a6cb..8b446a2fc2dd8 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher12.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher12.php @@ -63,8 +63,13 @@ public function match($rawPathinfo) list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - break; + if ('/' !== $pathinfo) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { + break; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + break; + } } foreach ($vars as $i => $v) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php index fc08f2b73e946..c4bf167f302cc 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php @@ -91,8 +91,13 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a } list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - return null; + if ('/' !== $pathinfo) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1]) { + return null; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + return null; + } } if ($requiredHost) { @@ -269,8 +274,13 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - return null; + if ('/' !== $pathinfo) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { + return null; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + return null; + } } foreach ($vars as $i => $v) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php index 291be1475760a..16ea1ad4e5dab 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php @@ -46,8 +46,13 @@ public function match($rawPathinfo) } list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - break; + if ('/' !== $pathinfo) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1]) { + break; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + break; + } } $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); @@ -82,8 +87,13 @@ public function match($rawPathinfo) list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - break; + if ('/' !== $pathinfo) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { + break; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + break; + } } foreach ($vars as $i => $v) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher4.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher4.php index 6bec91e309735..5e370d834506f 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher4.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher4.php @@ -66,8 +66,13 @@ public function match($rawPathinfo) } list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - break; + if ('/' !== $pathinfo) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1]) { + break; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + break; + } } $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher5.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher5.php index dee606f0a9bfa..6c01afb098f39 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher5.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher5.php @@ -83,8 +83,13 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a } list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - return null; + if ('/' !== $pathinfo) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1]) { + return null; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + return null; + } } $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); @@ -121,8 +126,13 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - return null; + if ('/' !== $pathinfo) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { + return null; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + return null; + } } foreach ($vars as $i => $v) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher6.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher6.php index 839481ba2db2d..746082cbbe8c3 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher6.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher6.php @@ -44,8 +44,13 @@ public function match($rawPathinfo) } list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - break; + if ('/' !== $pathinfo) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1]) { + break; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + break; + } } $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); @@ -98,8 +103,13 @@ public function match($rawPathinfo) list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - break; + if ('/' !== $pathinfo) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { + break; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + break; + } } foreach ($vars as $i => $v) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher7.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher7.php index 07c8af66697ef..68547bf3913f6 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher7.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher7.php @@ -79,8 +79,13 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a } list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - return null; + if ('/' !== $pathinfo) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1]) { + return null; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + return null; + } } $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); @@ -133,8 +138,13 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - return null; + if ('/' !== $pathinfo) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { + return null; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + return null; + } } foreach ($vars as $i => $v) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher8.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher8.php index ec5127e7b3a89..e8e71e427bc12 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher8.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher8.php @@ -51,8 +51,13 @@ public function match($rawPathinfo) list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - break; + if ('/' !== $pathinfo) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { + break; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + break; + } } foreach ($vars as $i => $v) { diff --git a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php index c1add96271dc2..1816ba197d982 100644 --- a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php +++ b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php @@ -680,6 +680,15 @@ public function testHostWithDot() $this->assertEquals('b', $matcher->match('/bar/abc.123')['_route']); } + public function testSlashVariant() + { + $coll = new RouteCollection(); + $coll->add('a', new Route('/foo/{bar}', array(), array('bar' => '.*'))); + + $matcher = $this->getUrlMatcher($coll); + $this->assertEquals('a', $matcher->match('/foo/')['_route']); + } + protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) { return new UrlMatcher($routes, $context ?: new RequestContext()); From a1e683b7ceadd22e1cd6a38bd233941d0ed6ab1b Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 30 Nov 2018 10:14:07 +0100 Subject: [PATCH 21/22] updated CHANGELOG for 4.2.0 --- CHANGELOG-4.2.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG-4.2.md b/CHANGELOG-4.2.md index d273a257a51dd..5e748a7913537 100644 --- a/CHANGELOG-4.2.md +++ b/CHANGELOG-4.2.md @@ -7,6 +7,16 @@ in 4.2 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v4.2.0...v4.2.1 +* 4.2.0 (2018-11-30) + + * bug #29343 [Form] Handle all case variants of "nan" when parsing a number (mwhudson, xabbuh) + * bug #29373 [Routing] fix trailing slash redirection (nicolas-grekas) + * bug #29355 [PropertyAccess] calculate cache keys for property setters depending on the value (xabbuh) + * bug #29369 [DI] fix combinatorial explosion when analyzing the service graph (nicolas-grekas) + * bug #29349 [Debug] workaround opcache bug mutating "$this" !?! (nicolas-grekas) + * bug #29344 Fixes sprintf(): Too few arguments in Translator (stephanedelprat) + * bug #29318 [Console] Move back root exception to stack trace in verbose mode (chalasr) + * 4.2.0-RC1 (2018-11-26) * bug #29332 [PropertyAccess] make cache keys encoding bijective (nicolas-grekas) From 887b0f13d753e342f5d22c1c1555fda28660c222 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 30 Nov 2018 10:16:14 +0100 Subject: [PATCH 22/22] updated VERSION for 4.2.0 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 7116b7015c465..225a047d141f3 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -73,12 +73,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private $requestStackSize = 0; private $resetServices = false; - const VERSION = '4.2.0-DEV'; + const VERSION = '4.2.0'; const VERSION_ID = 40200; const MAJOR_VERSION = 4; const MINOR_VERSION = 2; const RELEASE_VERSION = 0; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '07/2019'; const END_OF_LIFE = '01/2020';