diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index baf025cfe0..cd95879aaa 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -65,18 +65,18 @@ Symfony is the result of the work of many people who made the code better - Kevin Bond (kbond) - Saša Stamenković (umpirsky) - Peter Rehm (rpet) + - Gábor Egyed (1ed) - Gabriel Ostrolucký (gadelat) - Henrik Bjørnskov (henrikbjorn) - - Gábor Egyed (1ed) - Miha Vrhovnik - David Maicher (dmaicher) + - Titouan Galopin (tgalopin) - Diego Saint Esteben (dii3g0) - Jan Schädlich (jschaedl) - - Titouan Galopin (tgalopin) - Konstantin Kudryashov (everzet) + - Vladimir Reznichenko (kalessil) - Bilal Amarni (bamarni) - Mathieu Piot (mpiot) - - Vladimir Reznichenko (kalessil) - Florin Patan (florinpatan) - Jáchym Toušek (enumag) - Andrej Hudec (pulzarraider) @@ -91,12 +91,12 @@ Symfony is the result of the work of many people who made the code better - Henrik Westphal (snc) - Dariusz Górecki (canni) - David Buchmann (dbu) + - Jérôme Tamarelle (gromnan) - Graham Campbell (graham) - Dariusz Ruminski - Lee McDermott - Brandon Turner - Luis Cordova (cordoval) - - Jérôme Tamarelle (gromnan) - Daniel Holmes (dholmes) - Toni Uebernickel (havvg) - Fran Moreno (franmomu) @@ -106,11 +106,11 @@ Symfony is the result of the work of many people who made the code better - Antoine Hérault (herzult) - Paráda József (paradajozsef) - Arnaud Le Blanc (arnaud-lb) + - Sebastiaan Stok (sstok) - Maxime STEINHAUSSER - Baptiste Clavié (talus) - Michal Piotrowski (eventhorizon) - Tim Nagel (merk) - - Sebastiaan Stok (sstok) - Chris Wilkinson (thewilkybarkid) - Brice BERNARD (brikou) - marc.weistroff @@ -170,6 +170,7 @@ Symfony is the result of the work of many people who made the code better - Philipp Wahala (hifi) - Rafael Dohms (rdohms) - jwdeitch + - Ahmed TAILOULOUTE (ahmedtai) - Mikael Pajunen - Arman Hosseini (arman) - Niels Keurentjes (curry684) @@ -177,7 +178,6 @@ Symfony is the result of the work of many people who made the code better - Richard van Laak (rvanlaak) - Richard Shank (iampersistent) - Thomas Rabaix (rande) - - Ahmed TAILOULOUTE (ahmedtai) - Vincent Touzet (vincenttouzet) - jeremyFreeAgent (jeremyfreeagent) - Rouven Weßling (realityking) @@ -198,6 +198,7 @@ Symfony is the result of the work of many people who made the code better - GDIBass - Samuel NELA (snela) - Saif (╯°□°)╯ (azjezz) + - Gary PEGEOT (gary-p) - James Halsall (jaitsu) - Matthieu Napoli (mnapoli) - Florent Mata (fmata) @@ -220,7 +221,6 @@ Symfony is the result of the work of many people who made the code better - DQNEO - Andre Rømcke (andrerom) - mcfedr (mcfedr) - - Gary PEGEOT (gary-p) - Ruben Gonzalez (rubenrua) - Benjamin Dulau (dbenjamin) - Jan Rosier (rosier) @@ -244,6 +244,7 @@ Symfony is the result of the work of many people who made the code better - Matthieu Bontemps (mbontemps) - apetitpa - Pierre Minnieur (pminnieur) + - David Prévot - fivestar - Dominique Bongiraud - Jeremy Livingston (jeremylivingston) @@ -277,7 +278,6 @@ Symfony is the result of the work of many people who made the code better - Ruud Kamphuis (ruudk) - Pavel Batanov (scaytrase) - Mantis Development - - David Prévot - Loïc Faugeron - Hidde Wieringa (hiddewie) - dFayet @@ -371,6 +371,7 @@ Symfony is the result of the work of many people who made the code better - Chris Smith (cs278) - Thomas Bisignani (toma) - Florian Klein (docteurklein) + - Benjamin Leveque (benji07) - Manuel Kiessling (manuelkiessling) - Atsuhiro KUBO (iteman) - rudy onfroy (ronfroy) @@ -407,6 +408,7 @@ Symfony is the result of the work of many people who made the code better - Dariusz Rumiński - Berny Cantos (xphere81) - Thierry Thuon (lepiaf) + - Guilhem N (guilhemn) - Ricard Clau (ricardclau) - Mark Challoner (markchalloner) - Philippe Segatori @@ -432,6 +434,7 @@ Symfony is the result of the work of many people who made the code better - Tomasz Kowalczyk (thunderer) - Artur Eshenbrener - Timo Bakx (timobakx) + - Harm van Tilborg (hvt) - Thomas Perez (scullwm) - Felix Labrecque - Yaroslav Kiliba @@ -471,7 +474,6 @@ Symfony is the result of the work of many people who made the code better - Michele Locati - Pavel Volokitin (pvolok) - Valentine Boineau (valentineboineau) - - Benjamin Leveque (benji07) - Arthur de Moulins (4rthem) - Matthias Althaus (althaus) - Nicolas Dewez (nicolas_dewez) @@ -516,10 +518,11 @@ Symfony is the result of the work of many people who made the code better - Steffen Roßkamp - Alexandru Furculita (afurculita) - Valentin Jonovs (valentins-jonovs) - - Guilhem N (guilhemn) + - Sebastien Morel (plopix) - Jeanmonod David (jeanmonod) - Christopher Davis (chrisguitarguy) - Webnet team (webnet) + - Joe Bennett (kralos) - Farhad Safarov - Jan Schumann - Niklas Fiekas @@ -556,6 +559,7 @@ Symfony is the result of the work of many people who made the code better - Ariel Ferrandini (aferrandini) - Dirk Pahl (dirkaholic) - cedric lombardot (cedriclombardot) + - Arkadius Stefanski (arkadius) - Tim Goudriaan (codedmonkey) - Jonas Flodén (flojon) - Tobias Weichart @@ -621,6 +625,7 @@ Symfony is the result of the work of many people who made the code better - Maks Slesarenko - Filip Procházka (fprochazka) - mmoreram + - Jeroen Thora (bolle) - Markus Lanthaler (lanthaler) - Remi Collet - Vicent Soria Durá (vicentgodella) @@ -688,6 +693,7 @@ Symfony is the result of the work of many people who made the code better - Pavel Campr (pcampr) - Andrii Dembitskyi - Johnny Robeson (johnny) + - Thomas Landauer (thomas-landauer) - Guilliam Xavier - Disquedur - Michiel Boeckaert (milio) @@ -701,13 +707,11 @@ Symfony is the result of the work of many people who made the code better - Piotr Stankowski - Baptiste Leduc (bleduc) - Julien Maulny - - Sebastien Morel (plopix) - Jean-Christophe Cuvelier [Artack] - Julien Montel (julienmgel) - Simon DELICATA - Artem Henvald (artemgenvald) - Dmitry Simushev - - Joe Bennett (kralos) - alcaeus - Thomas Talbot (ioni) - Fred Cox @@ -722,10 +726,12 @@ Symfony is the result of the work of many people who made the code better - Marvin Butkereit - Renan - Ricky Su (ricky) + - Marcin Szepczynski (czepol) - Kyle Evans (kevans91) - Charles-Henri Bruyand - Max Rath (drak3) - Stéphane Escandell (sescandell) + - Baptiste Leduc (korbeil) - Konstantin S. M. Möllers (ksmmoellers) - James Johnston - Noémi Salaün (noemi-salaun) @@ -776,7 +782,6 @@ Symfony is the result of the work of many people who made the code better - maxime.steinhausser - adev - Stefan Warman - - Arkadius Stefanski (arkadius) - Tristan Maindron (tmaindron) - Behnoush Norouzali (behnoush) - Wesley Lancel @@ -949,12 +954,10 @@ Symfony is the result of the work of many people who made the code better - Jayson Xu (superjavason) - Hubert Lenoir (hubert_lenoir) - fago - - Harm van Tilborg - Jan Prieser - GDIBass - Antoine Lamirault - Adrien Lucas (adrienlucas) - - Jeroen Thora (bolle) - Zhuravlev Alexander (scif) - Stefano Degenkamp (steef) - James Michael DuPont @@ -1038,7 +1041,6 @@ Symfony is the result of the work of many people who made the code better - Don Pinkster - Maksim Muruev - Emil Einarsson - - Thomas Landauer - 243083df - Thibault Duplessis - Rimas Kudelis @@ -1060,6 +1062,7 @@ Symfony is the result of the work of many people who made the code better - Hugo Alliaume (kocal) - Marcos Gómez Vilches (markitosgv) - Matthew Davis (mdavis1982) + - Paulo Ribeiro (paulo) - Markus S. (staabm) - Benjamin Morel - Maks @@ -1118,7 +1121,6 @@ Symfony is the result of the work of many people who made the code better - xaav - Mahmoud Mostafa (mahmoud) - Antonio Jose Cerezo (ajcerezo) - - Baptiste Leduc (korbeil) - Ahmed Abdou - Daniel Iwaniec - Thomas Ferney @@ -1157,6 +1159,8 @@ Symfony is the result of the work of many people who made the code better - zairig imad (zairigimad) - Anton Babenko (antonbabenko) - Irmantas Šiupšinskas (irmantas) + - Benoit Mallo + - Lescot Edouard (idetox) - Danilo Silva - Giuseppe Campanelli - Arnaud PETITPAS (apetitpa) @@ -1226,11 +1230,13 @@ Symfony is the result of the work of many people who made the code better - gr1ev0us - mlazovla - Alejandro Diaz Torres + - quentin neyrat (qneyrat) - Max Beutel - Jan Vernieuwe (vernija) - Antanas Arvasevicius - Pierre Dudoret - Thomas + - j.schmitt - Maximilian Berghoff (electricmaxxx) - nacho - Piotr Antosik (antek88) @@ -1326,7 +1332,9 @@ Symfony is the result of the work of many people who made the code better - Carlos Ortega Huetos - rpg600 - Péter Buri (burci) + - John VanDeWeghe - kaiwa + - Claude Khedhiri (ck-developer) - Charles Sanquer (csanquer) - Albert Ganiev (helios-ag) - Neil Katin @@ -1363,6 +1371,7 @@ Symfony is the result of the work of many people who made the code better - arnaud (arnooo999) - Gilles Doge (gido) - Oscar Esteve (oesteve) + - Sobhan Sharifi (50bhan) - abulford - Philipp Kretzschmar - antograssiot @@ -1412,6 +1421,7 @@ Symfony is the result of the work of many people who made the code better - Marc Torres - Mark Spink - Alberto Aldegheri + - Sagrario Meneses - Dmitri Petmanson - heccjj - Alexandre Melard @@ -1731,7 +1741,9 @@ Symfony is the result of the work of many people who made the code better - Przemysław Piechota (kibao) - Leonid Terentyev (li0n) - Martynas Sudintas (martiis) + - Douglas Hammond (wizhippo) - ryunosuke + - Bruno BOUTAREL - victoria - Francisco Facioni (fran6co) - Stanislav Gamayunov (happyproff) @@ -1864,6 +1876,7 @@ Symfony is the result of the work of many people who made the code better - Pavel.Batanov - avi123 - Pavel Prischepa + - Sami Mussbach - alsar - downace - Aarón Nieves Fernández @@ -1891,6 +1904,7 @@ Symfony is the result of the work of many people who made the code better - Brian Graham (incognito) - Kevin Vergauwen (innocenzo) - Alessio Baglio (ioalessio) + - Jeroen Noten (jeroennoten) - Johannes Müller (johmue) - Jordi Llonch (jordillonch) - Nicholas Ruunu (nicholasruunu) @@ -1911,6 +1925,7 @@ Symfony is the result of the work of many people who made the code better - Alexey Popkov - Gijs Kunze - Artyom Protaskin + - Steven Dubois - Nathanael d. Noblet - helmer - ged15 @@ -1983,6 +1998,7 @@ Symfony is the result of the work of many people who made the code better - Andrew Marcinkevičius (ifdattic) - Ioana Hazsda (ioana-hazsda) - Jan Marek (janmarek) + - Dmitriy Mamontov (mamontovdmitriy) - Mark de Haan (markdehaan) - Dan Patrick (mdpatrick) - naitsirch (naitsirch) @@ -2343,6 +2359,7 @@ Symfony is the result of the work of many people who made the code better - Ben Miller - Peter Gribanov - Matteo Galli + - Loenix - kwiateusz - jspee - Ilya Bulakh @@ -2466,7 +2483,6 @@ Symfony is the result of the work of many people who made the code better - Marco Petersen (ocrampete16) - ollie harridge (ollietb) - Paul Andrieux (paulandrieux) - - Paulo Ribeiro (paulo) - Paweł Szczepanek (pauluz) - Philippe Degeeter (pdegeeter) - Christian López Espínola (penyaskito) @@ -2521,6 +2537,7 @@ Symfony is the result of the work of many people who made the code better - MaPePeR - Andreas Streichardt - Alexandre Segura + - Marco Pfeiffer - Vivien - Pascal Hofmann - david-binda diff --git a/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/NotFoundActivationStrategy.php b/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/NotFoundActivationStrategy.php index 4bea3cc8a8..f73cd2f41b 100644 --- a/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/NotFoundActivationStrategy.php +++ b/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/NotFoundActivationStrategy.php @@ -23,7 +23,7 @@ use Symfony\Component\HttpKernel\Exception\HttpException; */ class NotFoundActivationStrategy extends ErrorLevelActivationStrategy { - private $blacklist; + private $exclude; private $requestStack; public function __construct(RequestStack $requestStack, array $excludedUrls, $actionLevel) @@ -31,7 +31,7 @@ class NotFoundActivationStrategy extends ErrorLevelActivationStrategy parent::__construct($actionLevel); $this->requestStack = $requestStack; - $this->blacklist = '{('.implode('|', $excludedUrls).')}i'; + $this->exclude = '{('.implode('|', $excludedUrls).')}i'; } public function isHandlerActivated(array $record): bool @@ -45,7 +45,7 @@ class NotFoundActivationStrategy extends ErrorLevelActivationStrategy && 404 == $record['context']['exception']->getStatusCode() && ($request = $this->requestStack->getMasterRequest()) ) { - return !preg_match($this->blacklist, $request->getPathInfo()); + return !preg_match($this->exclude, $request->getPathInfo()); } return $isActivated; diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php index ca41113b37..d78e848030 100644 --- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php +++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php @@ -182,15 +182,21 @@ if (!file_exists("$PHPUNIT_DIR/$PHPUNIT_VERSION_DIR/phpunit") || $configurationH } } + if (in_array('--colors=never', $argv, true) || (isset($argv[$i = array_search('never', $argv, true) - 1]) && '--colors' === $argv[$i])) { + $COMPOSER .= ' --no-ansi'; + } else { + $COMPOSER .= ' --ansi'; + } + $info += [ 'versions' => [], 'requires' => ['php' => '*'], ]; if (1 === \count($info['versions'])) { - $passthruOrFail("$COMPOSER create-project --ignore-platform-reqs --no-install --prefer-dist --no-scripts --no-plugins --no-progress --ansi -s dev phpunit/phpunit $PHPUNIT_VERSION_DIR \"$PHPUNIT_VERSION.*\""); + $passthruOrFail("$COMPOSER create-project --ignore-platform-reqs --no-install --prefer-dist --no-scripts --no-plugins --no-progress -s dev phpunit/phpunit $PHPUNIT_VERSION_DIR \"$PHPUNIT_VERSION.*\""); } else { - $passthruOrFail("$COMPOSER create-project --ignore-platform-reqs --no-install --prefer-dist --no-scripts --no-plugins --no-progress --ansi phpunit/phpunit $PHPUNIT_VERSION_DIR \"$PHPUNIT_VERSION.*\""); + $passthruOrFail("$COMPOSER create-project --ignore-platform-reqs --no-install --prefer-dist --no-scripts --no-plugins --no-progress phpunit/phpunit $PHPUNIT_VERSION_DIR \"$PHPUNIT_VERSION.*\""); } @copy("$PHPUNIT_VERSION_DIR/phpunit.xsd", 'phpunit.xsd'); @@ -220,7 +226,7 @@ if (!file_exists("$PHPUNIT_DIR/$PHPUNIT_VERSION_DIR/phpunit") || $configurationH putenv("COMPOSER_ROOT_VERSION=$PHPUNIT_VERSION.99"); $q = '\\' === DIRECTORY_SEPARATOR ? '"' : ''; // --no-suggest is not in the list to keep compat with composer 1.0, which is shipped with Ubuntu 16.04LTS - $exit = proc_close(proc_open("$q$COMPOSER install --no-dev --prefer-dist --no-progress --ansi$q", [], $p, getcwd())); + $exit = proc_close(proc_open("$q$COMPOSER install --no-dev --prefer-dist --no-progress $q", [], $p, getcwd())); putenv('COMPOSER_ROOT_VERSION'.(false !== $prevRoot ? '='.$prevRoot : '')); if ($exit) { exit($exit); diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php b/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php index 00d1a4a6a1..58f06d5df9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php @@ -62,7 +62,17 @@ class RedirectController $attributes = []; if (false === $ignoreAttributes || \is_array($ignoreAttributes)) { $attributes = $request->attributes->get('_route_params'); - $attributes = $keepQueryParams ? array_merge($request->query->all(), $attributes) : $attributes; + + if ($keepQueryParams) { + if ($query = $request->server->get('QUERY_STRING')) { + $query = self::parseQuery($query); + } else { + $query = $request->query->all(); + } + + $attributes = array_merge($query, $attributes); + } + unset($attributes['route'], $attributes['permanent'], $attributes['ignoreAttributes'], $attributes['keepRequestMethod'], $attributes['keepQueryParams']); if ($ignoreAttributes) { $attributes = array_diff_key($attributes, array_flip($ignoreAttributes)); @@ -117,8 +127,7 @@ class RedirectController $scheme = $request->getScheme(); } - $qs = $request->getQueryString(); - if ($qs) { + if ($qs = $request->server->get('QUERY_STRING') ?: $request->getQueryString()) { if (false === strpos($path, '?')) { $qs = '?'.$qs; } else { @@ -176,4 +185,49 @@ class RedirectController throw new \RuntimeException(sprintf('The parameter "path" or "route" is required to configure the redirect action in "%s" routing configuration.', $request->attributes->get('_route'))); } + + private static function parseQuery(string $query) + { + $q = []; + + foreach (explode('&', $query) as $v) { + if (false !== $i = strpos($v, "\0")) { + $v = substr($v, 0, $i); + } + + if (false === $i = strpos($v, '=')) { + $k = urldecode($v); + $v = ''; + } else { + $k = urldecode(substr($v, 0, $i)); + $v = substr($v, $i); + } + + if (false !== $i = strpos($k, "\0")) { + $k = substr($k, 0, $i); + } + + $k = ltrim($k, ' '); + + if (false === $i = strpos($k, '[')) { + $q[] = bin2hex($k).$v; + } else { + $q[] = substr_replace($k, bin2hex(substr($k, 0, $i)), 0, $i).$v; + } + } + + parse_str(implode('&', $q), $q); + + $query = []; + + foreach ($q as $k => $v) { + if (false !== $i = strpos($k, '_')) { + $query[substr_replace($k, hex2bin(substr($k, 0, $i)).'[', 0, 1 + $i)] = $v; + } else { + $query[hex2bin($k)] = $v; + } + } + + return $query; + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php index c8942614d1..732ac3e2d6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php @@ -267,9 +267,9 @@ class RedirectControllerTest extends TestCase return [ ['http://www.example.com/base/redirect-path', '/redirect-path', ''], ['http://www.example.com/base/redirect-path?foo=bar', '/redirect-path?foo=bar', ''], - ['http://www.example.com/base/redirect-path?foo=bar', '/redirect-path', 'foo=bar'], - ['http://www.example.com/base/redirect-path?foo=bar&abc=example', '/redirect-path?foo=bar', 'abc=example'], - ['http://www.example.com/base/redirect-path?foo=bar&abc=example&baz=def', '/redirect-path?foo=bar', 'abc=example&baz=def'], + ['http://www.example.com/base/redirect-path?f.o=bar', '/redirect-path', 'f.o=bar'], + ['http://www.example.com/base/redirect-path?f.o=bar&a.c=example', '/redirect-path?f.o=bar', 'a.c=example'], + ['http://www.example.com/base/redirect-path?f.o=bar&a.c=example&b.z=def', '/redirect-path?f.o=bar', 'a.c=example&b.z=def'], ]; } @@ -302,17 +302,16 @@ class RedirectControllerTest extends TestCase $baseUrl = '/base'; $port = 80; - $request = $this->createRequestObject($scheme, $host, $port, $baseUrl, 'base=zaza'); - $request->query = new ParameterBag(['base' => 'zaza']); + $request = $this->createRequestObject($scheme, $host, $port, $baseUrl, 'b.se=zaza'); $request->attributes = new ParameterBag(['_route_params' => ['base2' => 'zaza']]); $urlGenerator = $this->getMockBuilder(UrlGeneratorInterface::class)->getMock(); - $urlGenerator->expects($this->exactly(2))->method('generate')->willReturn('/test?base=zaza&base2=zaza')->with('/test', ['base' => 'zaza', 'base2' => 'zaza'], UrlGeneratorInterface::ABSOLUTE_URL); + $urlGenerator->expects($this->exactly(2))->method('generate')->willReturn('/test?b.se=zaza&base2=zaza')->with('/test', ['b.se' => 'zaza', 'base2' => 'zaza'], UrlGeneratorInterface::ABSOLUTE_URL); $controller = new RedirectController($urlGenerator); - $this->assertRedirectUrl($controller->redirectAction($request, '/test', false, false, false, true), '/test?base=zaza&base2=zaza'); + $this->assertRedirectUrl($controller->redirectAction($request, '/test', false, false, false, true), '/test?b.se=zaza&base2=zaza'); $request->attributes->set('_route_params', ['base2' => 'zaza', 'route' => '/test', 'ignoreAttributes' => false, 'keepRequestMethod' => false, 'keepQueryParams' => true]); - $this->assertRedirectUrl($controller($request), '/test?base=zaza&base2=zaza'); + $this->assertRedirectUrl($controller($request), '/test?b.se=zaza&base2=zaza'); } public function testRedirectWithQueryWithRouteParamsOveriding() @@ -322,17 +321,16 @@ class RedirectControllerTest extends TestCase $baseUrl = '/base'; $port = 80; - $request = $this->createRequestObject($scheme, $host, $port, $baseUrl, 'base=zaza'); - $request->query = new ParameterBag(['base' => 'zaza']); - $request->attributes = new ParameterBag(['_route_params' => ['base' => 'zouzou']]); + $request = $this->createRequestObject($scheme, $host, $port, $baseUrl, 'b.se=zaza'); + $request->attributes = new ParameterBag(['_route_params' => ['b.se' => 'zouzou']]); $urlGenerator = $this->getMockBuilder(UrlGeneratorInterface::class)->getMock(); - $urlGenerator->expects($this->exactly(2))->method('generate')->willReturn('/test?base=zouzou')->with('/test', ['base' => 'zouzou'], UrlGeneratorInterface::ABSOLUTE_URL); + $urlGenerator->expects($this->exactly(2))->method('generate')->willReturn('/test?b.se=zouzou')->with('/test', ['b.se' => 'zouzou'], UrlGeneratorInterface::ABSOLUTE_URL); $controller = new RedirectController($urlGenerator); - $this->assertRedirectUrl($controller->redirectAction($request, '/test', false, false, false, true), '/test?base=zouzou'); + $this->assertRedirectUrl($controller->redirectAction($request, '/test', false, false, false, true), '/test?b.se=zouzou'); - $request->attributes->set('_route_params', ['base' => 'zouzou', 'route' => '/test', 'ignoreAttributes' => false, 'keepRequestMethod' => false, 'keepQueryParams' => true]); - $this->assertRedirectUrl($controller($request), '/test?base=zouzou'); + $request->attributes->set('_route_params', ['b.se' => 'zouzou', 'route' => '/test', 'ignoreAttributes' => false, 'keepRequestMethod' => false, 'keepQueryParams' => true]); + $this->assertRedirectUrl($controller($request), '/test?b.se=zouzou'); } public function testMissingPathOrRouteParameter() @@ -353,29 +351,20 @@ class RedirectControllerTest extends TestCase private function createRequestObject($scheme, $host, $port, $baseUrl, $queryString = '') { - $request = $this->getMockBuilder('Symfony\Component\HttpFoundation\Request')->getMock(); - $request - ->expects($this->any()) - ->method('getScheme') - ->willReturn($scheme); - $request - ->expects($this->any()) - ->method('getHost') - ->willReturn($host); - $request - ->expects($this->any()) - ->method('getPort') - ->willReturn($port); - $request - ->expects($this->any()) - ->method('getBaseUrl') - ->willReturn($baseUrl); - $request - ->expects($this->any()) - ->method('getQueryString') - ->willReturn($queryString); + if ('' !== $queryString) { + parse_str($queryString, $query); + } else { + $query = []; + } - return $request; + return new Request($query, [], [], [], [], [ + 'HTTPS' => 'https' === $scheme, + 'HTTP_HOST' => $host.($port ? ':'.$port : ''), + 'SERVER_PORT' => $port, + 'SCRIPT_FILENAME' => $baseUrl, + 'REQUEST_URI' => $baseUrl, + 'QUERY_STRING' => $queryString, + ]); } private function createRedirectController($httpPort = null, $httpsPort = null) diff --git a/src/Symfony/Component/Cache/Adapter/PdoAdapter.php b/src/Symfony/Component/Cache/Adapter/PdoAdapter.php index 97ff79ec28..26804dfc76 100644 --- a/src/Symfony/Component/Cache/Adapter/PdoAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/PdoAdapter.php @@ -435,26 +435,31 @@ class PdoAdapter extends AbstractAdapter implements PruneableInterface if ($this->conn instanceof \PDO) { $this->driver = $this->conn->getAttribute(\PDO::ATTR_DRIVER_NAME); } else { - switch ($this->driver = $this->conn->getDriver()->getName()) { - case 'mysqli': + $driver = $this->conn->getDriver(); + + switch (true) { + case $driver instanceof \Doctrine\DBAL\Driver\Mysqli\Driver: throw new \LogicException(sprintf('The adapter "%s" does not support the mysqli driver, use pdo_mysql instead.', static::class)); - case 'pdo_mysql': - case 'drizzle_pdo_mysql': + + case $driver instanceof \Doctrine\DBAL\Driver\AbstractMySQLDriver: $this->driver = 'mysql'; break; - case 'pdo_sqlite': + case $driver instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver: $this->driver = 'sqlite'; break; - case 'pdo_pgsql': + case $driver instanceof \Doctrine\DBAL\Driver\PDOPgSql\Driver: $this->driver = 'pgsql'; break; - case 'oci8': - case 'pdo_oracle': + case $driver instanceof \Doctrine\DBAL\Driver\OCI8\Driver: + case $driver instanceof \Doctrine\DBAL\Driver\PDOOracle\Driver: $this->driver = 'oci'; break; - case 'pdo_sqlsrv': + case $driver instanceof \Doctrine\DBAL\Driver\SQLSrv\Driver: $this->driver = 'sqlsrv'; break; + default: + $this->driver = \get_class($driver); + break; } } } diff --git a/src/Symfony/Component/Config/Resource/ReflectionClassResource.php b/src/Symfony/Component/Config/Resource/ReflectionClassResource.php index 602b0e7711..bcf807e801 100644 --- a/src/Symfony/Component/Config/Resource/ReflectionClassResource.php +++ b/src/Symfony/Component/Config/Resource/ReflectionClassResource.php @@ -169,6 +169,7 @@ class ReflectionClassResource implements SelfCheckingResourceInterface if (!$parametersWithUndefinedConstants) { yield preg_replace('/^ @@.*/m', '', $m); } else { + $t = $m->getReturnType(); $stack = [ $m->getDocComment(), $m->getName(), @@ -179,15 +180,16 @@ class ReflectionClassResource implements SelfCheckingResourceInterface $m->isPrivate(), $m->isProtected(), $m->returnsReference(), - $m->hasReturnType() ? $m->getReturnType()->getName() : '', + $t instanceof \ReflectionNamedType ? ((string) $t->allowsNull()).$t->getName() : (string) $t, ]; foreach ($m->getParameters() as $p) { if (!isset($parametersWithUndefinedConstants[$p->name])) { $stack[] = (string) $p; } else { + $t = $p->getType(); $stack[] = $p->isOptional(); - $stack[] = $p->hasType() ? $p->getType()->getName() : ''; + $stack[] = $t instanceof \ReflectionNamedType ? ((string) $t->allowsNull()).$t->getName() : (string) $t; $stack[] = $p->isPassedByReference(); $stack[] = $p->isVariadic(); $stack[] = $p->getName(); diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php index 8229beb106..f4974cce10 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php @@ -153,9 +153,26 @@ final class CheckTypeDeclarationsPass extends AbstractRecursivePass /** * @throws InvalidParameterTypeException When a parameter is not compatible with the declared type */ - private function checkType(Definition $checkedDefinition, $value, \ReflectionParameter $parameter, ?string $envPlaceholderUniquePrefix): void + private function checkType(Definition $checkedDefinition, $value, \ReflectionParameter $parameter, ?string $envPlaceholderUniquePrefix, string $type = null): void { - $type = $parameter->getType()->getName(); + if (null === $type) { + $type = $parameter->getType(); + + if ($type instanceof \ReflectionUnionType) { + foreach ($type->getTypes() as $type) { + try { + $this->checkType($checkedDefinition, $value, $parameter, $envPlaceholderUniquePrefix, $type); + + return; + } catch (InvalidParameterTypeException $e) { + } + } + + throw new InvalidParameterTypeException($this->currentId, $e->getCode(), $parameter); + } + + $type = $type->getName(); + } if ($value instanceof Reference) { if (!$this->container->has($value = (string) $value)) { @@ -266,7 +283,7 @@ final class CheckTypeDeclarationsPass extends AbstractRecursivePass return; } - $checkFunction = sprintf('is_%s', $parameter->getType()->getName()); + $checkFunction = sprintf('is_%s', $type); if (!$parameter->getType()->isBuiltin() || !$checkFunction($value)) { throw new InvalidParameterTypeException($this->currentId, \is_object($value) ? $class : get_debug_type($value), $parameter); diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 28e475c460..671b787624 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -227,7 +227,7 @@ class PhpDumper extends Dumper $regex = preg_quote(\DIRECTORY_SEPARATOR.$dir[$i], '#').$regex; } while (0 < --$i); - $this->targetDirRegex = '#'.preg_quote($dir[0], '#').$regex.'#'; + $this->targetDirRegex = '#(^|file://|[:;, \|\r\n])'.preg_quote($dir[0], '#').$regex.'#'; } } @@ -2030,11 +2030,12 @@ EOF; private function export($value) { if (null !== $this->targetDirRegex && \is_string($value) && preg_match($this->targetDirRegex, $value, $matches, PREG_OFFSET_CAPTURE)) { - $prefix = $matches[0][1] ? $this->doExport(substr($value, 0, $matches[0][1]), true).'.' : ''; $suffix = $matches[0][1] + \strlen($matches[0][0]); + $matches[0][1] += \strlen($matches[1][0]); + $prefix = $matches[0][1] ? $this->doExport(substr($value, 0, $matches[0][1]), true).'.' : ''; $suffix = isset($value[$suffix]) ? '.'.$this->doExport(substr($value, $suffix), true) : ''; $dirname = $this->asFiles ? '$this->containerDir' : '__DIR__'; - $offset = 1 + $this->targetDirMaxMatches - \count($matches); + $offset = 2 + $this->targetDirMaxMatches - \count($matches); if (0 < $offset) { $dirname = sprintf('\dirname(__DIR__, %d)', $offset + (int) $this->asFiles); diff --git a/src/Symfony/Component/DependencyInjection/Dumper/Preloader.php b/src/Symfony/Component/DependencyInjection/Dumper/Preloader.php index c2c805861b..939dedf09a 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/Preloader.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/Preloader.php @@ -67,7 +67,7 @@ final class Preloader } } - private static function doPreload(string $class, array &$preloaded) + private static function doPreload(string $class, array &$preloaded): void { if (isset($preloaded[$class]) || \in_array($class, ['self', 'static', 'parent'], true)) { return; @@ -87,9 +87,7 @@ final class Preloader if (\PHP_VERSION_ID >= 70400) { foreach ($r->getProperties(\ReflectionProperty::IS_PUBLIC) as $p) { - if (($t = $p->getType()) && !$t->isBuiltin()) { - self::doPreload($t->getName(), $preloaded); - } + self::preloadType($p->getType(), $preloaded); } } @@ -103,17 +101,26 @@ final class Preloader } } - if (($t = $p->getType()) && !$t->isBuiltin()) { - self::doPreload($t->getName(), $preloaded); - } + self::preloadType($p->getType(), $preloaded); } - if (($t = $m->getReturnType()) && !$t->isBuiltin()) { - self::doPreload($t->getName(), $preloaded); - } + self::preloadType($p->getReturnType(), $preloaded); } } catch (\ReflectionException $e) { // ignore missing classes } } + + private static function preloadType(?\ReflectionType $t, array &$preloaded): void + { + if (!$t || $t->isBuiltin()) { + return; + } + + foreach ($t instanceof \ReflectionUnionType ? $t->getTypes() : [$t] as $t) { + if (!$t->isBuiltin()) { + self::doPreload($t instanceof \ReflectionNamedType ? $t->getName() : $t, $preloaded); + } + } + } } diff --git a/src/Symfony/Component/DependencyInjection/Exception/InvalidParameterTypeException.php b/src/Symfony/Component/DependencyInjection/Exception/InvalidParameterTypeException.php index 206561fa95..05fdb4a779 100644 --- a/src/Symfony/Component/DependencyInjection/Exception/InvalidParameterTypeException.php +++ b/src/Symfony/Component/DependencyInjection/Exception/InvalidParameterTypeException.php @@ -21,6 +21,9 @@ class InvalidParameterTypeException extends InvalidArgumentException { public function __construct(string $serviceId, string $type, \ReflectionParameter $parameter) { - parent::__construct(sprintf('Invalid definition for service "%s": argument %d of "%s::%s" accepts "%s", "%s" passed.', $serviceId, 1 + $parameter->getPosition(), $parameter->getDeclaringClass()->getName(), $parameter->getDeclaringFunction()->getName(), $parameter->getType()->getName(), $type)); + $acceptedType = $parameter->getType(); + $acceptedType = $acceptedType instanceof \ReflectionNamedType ? $acceptedType->getName() : (string) $acceptedType; + + parent::__construct(sprintf('Invalid definition for service "%s": argument %d of "%s::%s" accepts "%s", "%s" passed.', $serviceId, 1 + $parameter->getPosition(), $parameter->getDeclaringClass()->getName(), $parameter->getDeclaringFunction()->getName(), $acceptedType, $type), $type); } } diff --git a/src/Symfony/Component/DependencyInjection/LazyProxy/ProxyHelper.php b/src/Symfony/Component/DependencyInjection/LazyProxy/ProxyHelper.php index e5611bc493..f4a5afbb89 100644 --- a/src/Symfony/Component/DependencyInjection/LazyProxy/ProxyHelper.php +++ b/src/Symfony/Component/DependencyInjection/LazyProxy/ProxyHelper.php @@ -31,26 +31,36 @@ class ProxyHelper if (!$type) { return null; } - if (!\is_string($type)) { - $name = $type->getName(); + + $types = []; + + foreach ($type instanceof \ReflectionUnionType ? $type->getTypes() : [$type] as $type) { + $name = $type instanceof \ReflectionNamedType ? $type->getName() : (string) $type; if ($type->isBuiltin()) { - return $noBuiltin ? null : $name; + if (!$noBuiltin) { + $types[] = $name; + } + continue; + } + + $lcName = strtolower($name); + $prefix = $noBuiltin ? '' : '\\'; + + if ('self' !== $lcName && 'parent' !== $lcName) { + $types[] = '' !== $prefix ? $prefix.$name : $name; + continue; + } + if (!$r instanceof \ReflectionMethod) { + continue; + } + if ('self' === $lcName) { + $types[] = $prefix.$r->getDeclaringClass()->name; + } else { + $types[] = ($parent = $r->getDeclaringClass()->getParentClass()) ? $prefix.$parent->name : null; } } - $lcName = strtolower($name); - $prefix = $noBuiltin ? '' : '\\'; - if ('self' !== $lcName && 'parent' !== $lcName) { - return $prefix.$name; - } - if (!$r instanceof \ReflectionMethod) { - return null; - } - if ('self' === $lcName) { - return $prefix.$r->getDeclaringClass()->name; - } - - return ($parent = $r->getDeclaringClass()->getParentClass()) ? $prefix.$parent->name : null; + return $types ? implode('|', $types) : null; } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 93000ab82e..0122094acd 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -113,7 +113,7 @@ class PhpDumperTest extends TestCase $container = new ContainerBuilder(); $container->setDefinition('test', $definition); - $container->setParameter('foo', 'wiz'.\dirname(__DIR__)); + $container->setParameter('foo', 'file://'.\dirname(__DIR__)); $container->setParameter('bar', __DIR__); $container->setParameter('baz', '%bar%/PhpDumperTest.php'); $container->setParameter('buz', \dirname(__DIR__, 2)); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php index 1fec4f85b2..b87194357e 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php @@ -53,7 +53,7 @@ class ProjectServiceContainer extends Container */ protected function getTestService() { - return $this->services['test'] = new \stdClass(('wiz'.\dirname(__DIR__, 1)), [('wiz'.\dirname(__DIR__, 1)) => (\dirname(__DIR__, 2).'/')]); + return $this->services['test'] = new \stdClass(('file://'.\dirname(__DIR__, 1)), [('file://'.\dirname(__DIR__, 1)) => (\dirname(__DIR__, 2).'/')]); } public function getParameter(string $name) @@ -102,7 +102,7 @@ class ProjectServiceContainer extends Container protected function getDefaultParameters(): array { return [ - 'foo' => ('wiz'.\dirname(__DIR__, 1)), + 'foo' => ('file://'.\dirname(__DIR__, 1)), 'bar' => __DIR__, 'baz' => (__DIR__.'/PhpDumperTest.php'), 'buz' => \dirname(__DIR__, 2), diff --git a/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php b/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php index c2b8b0d418..a40cee00bb 100644 --- a/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php +++ b/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php @@ -187,7 +187,7 @@ class RegisterListenersPass implements CompilerPassInterface || !($r = $container->getReflectionClass($class, false)) || !$r->hasMethod($method) || 1 > ($m = $r->getMethod($method))->getNumberOfParameters() - || !($type = $m->getParameters()[0]->getType()) + || !($type = $m->getParameters()[0]->getType()) instanceof \ReflectionNamedType || $type->isBuiltin() || Event::class === ($name = $type->getName()) ) { diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php index 9bf1ac27f2..671dc67d2f 100644 --- a/src/Symfony/Component/HttpClient/CurlHttpClient.php +++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php @@ -13,7 +13,6 @@ namespace Symfony\Component\HttpClient; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareTrait; -use Psr\Log\LoggerInterface; use Symfony\Component\HttpClient\Exception\InvalidArgumentException; use Symfony\Component\HttpClient\Exception\TransportException; use Symfony\Component\HttpClient\Internal\CurlClientState; @@ -71,7 +70,7 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface, [, $this->defaultOptions] = self::prepareRequest(null, null, $defaultOptions, $this->defaultOptions); } - $this->multi = $multi = new CurlClientState(); + $this->multi = new CurlClientState(); self::$curlVersion = self::$curlVersion ?? curl_version(); // Don't enable HTTP/1.1 pipelining: it forces responses to be sent in order @@ -95,10 +94,8 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface, return; } - $logger = &$this->logger; - - curl_multi_setopt($this->multi->handle, CURLMOPT_PUSHFUNCTION, static function ($parent, $pushed, array $requestHeaders) use ($multi, $maxPendingPushes, &$logger) { - return self::handlePush($parent, $pushed, $requestHeaders, $multi, $maxPendingPushes, $logger); + curl_multi_setopt($this->multi->handle, CURLMOPT_PUSHFUNCTION, function ($parent, $pushed, array $requestHeaders) use ($maxPendingPushes) { + return $this->handlePush($parent, $pushed, $requestHeaders, $maxPendingPushes); }); } @@ -361,7 +358,7 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface, $this->reset(); } - private static function handlePush($parent, $pushed, array $requestHeaders, CurlClientState $multi, int $maxPendingPushes, ?LoggerInterface $logger): int + private function handlePush($parent, $pushed, array $requestHeaders, int $maxPendingPushes): int { $headers = []; $origin = curl_getinfo($parent, CURLINFO_EFFECTIVE_URL); @@ -373,7 +370,7 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface, } if (!isset($headers[':method']) || !isset($headers[':scheme']) || !isset($headers[':authority']) || !isset($headers[':path'])) { - $logger && $logger->debug(sprintf('Rejecting pushed response from "%s": pushed headers are invalid', $origin)); + $this->logger && $this->logger->debug(sprintf('Rejecting pushed response from "%s": pushed headers are invalid', $origin)); return CURL_PUSH_DENY; } @@ -384,21 +381,21 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface, // but this is a MUST in the HTTP/2 RFC; let's restrict pushes to the original host, // ignoring domains mentioned as alt-name in the certificate for now (same as curl). if (0 !== strpos($origin, $url.'/')) { - $logger && $logger->debug(sprintf('Rejecting pushed response from "%s": server is not authoritative for "%s"', $origin, $url)); + $this->logger && $this->logger->debug(sprintf('Rejecting pushed response from "%s": server is not authoritative for "%s"', $origin, $url)); return CURL_PUSH_DENY; } - if ($maxPendingPushes <= \count($multi->pushedResponses)) { - $fifoUrl = key($multi->pushedResponses); - unset($multi->pushedResponses[$fifoUrl]); - $logger && $logger->debug(sprintf('Evicting oldest pushed response: "%s"', $fifoUrl)); + if ($maxPendingPushes <= \count($this->multi->pushedResponses)) { + $fifoUrl = key($this->multi->pushedResponses); + unset($this->multi->pushedResponses[$fifoUrl]); + $this->logger && $this->logger->debug(sprintf('Evicting oldest pushed response: "%s"', $fifoUrl)); } $url .= $headers[':path'][0]; - $logger && $logger->debug(sprintf('Queueing pushed response: "%s"', $url)); + $this->logger && $this->logger->debug(sprintf('Queueing pushed response: "%s"', $url)); - $multi->pushedResponses[$url] = new PushedResponse(new CurlResponse($multi, $pushed), $headers, $multi->openHandles[(int) $parent][1] ?? [], $pushed); + $this->multi->pushedResponses[$url] = new PushedResponse(new CurlResponse($this->multi, $pushed), $headers, $this->multi->openHandles[(int) $parent][1] ?? [], $pushed); return CURL_PUSH_OK; } diff --git a/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php b/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php index 9370174c25..6c192a6f5f 100644 --- a/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php +++ b/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php @@ -45,7 +45,7 @@ final class ArgumentMetadataFactory implements ArgumentMetadataFactoryInterface */ private function getType(\ReflectionParameter $parameter, \ReflectionFunctionAbstract $function): ?string { - if (!$type = $parameter->getType()) { + if (!($type = $parameter->getType()) instanceof \ReflectionNamedType) { return null; } $name = $type->getName(); diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/AddAnnotatedClassesToCachePass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/AddAnnotatedClassesToCachePass.php index 70a987ebf4..5eb833b51d 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/AddAnnotatedClassesToCachePass.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/AddAnnotatedClassesToCachePass.php @@ -127,10 +127,10 @@ class AddAnnotatedClassesToCachePass implements CompilerPassInterface private function matchAnyRegexps(string $class, array $regexps): bool { - $blacklisted = false !== strpos($class, 'Test'); + $isTest = false !== strpos($class, 'Test'); foreach ($regexps as $regex) { - if ($blacklisted && false === strpos($regex, 'Test')) { + if ($isTest && false === strpos($regex, 'Test')) { continue; } diff --git a/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php b/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php index 6192a452e3..f5cac76bd8 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php @@ -102,7 +102,7 @@ class ErrorListener implements EventSubscriberInterface $r = new \ReflectionFunction(\Closure::fromCallable($event->getController())); $r = $r->getParameters()[$k] ?? null; - if ($r && (!$r->hasType() || \in_array($r->getType()->getName(), [FlattenException::class, LegacyFlattenException::class], true))) { + if ($r && (!($r = $r->getType()) instanceof \ReflectionNamedType || \in_array($r->getName(), [FlattenException::class, LegacyFlattenException::class], true))) { $arguments = $event->getArguments(); $arguments[$k] = FlattenException::createFromThrowable($e); $event->setArguments($arguments); diff --git a/src/Symfony/Component/Intl/Data/Generator/CurrencyDataGenerator.php b/src/Symfony/Component/Intl/Data/Generator/CurrencyDataGenerator.php index e7c28bc24f..2c9c31b58c 100644 --- a/src/Symfony/Component/Intl/Data/Generator/CurrencyDataGenerator.php +++ b/src/Symfony/Component/Intl/Data/Generator/CurrencyDataGenerator.php @@ -25,7 +25,7 @@ use Symfony\Component\Intl\Data\Util\LocaleScanner; */ class CurrencyDataGenerator extends AbstractDataGenerator { - private static $blacklist = [ + private static $denylist = [ 'XBA' => true, // European Composite Unit 'XBB' => true, // European Monetary Unit 'XBC' => true, // European Unit of Account (XBC) @@ -133,7 +133,7 @@ class CurrencyDataGenerator extends AbstractDataGenerator $symbolNamePairs = iterator_to_array($rootBundle['Currencies']); // Remove unwanted currencies - $symbolNamePairs = array_diff_key($symbolNamePairs, self::$blacklist); + $symbolNamePairs = array_diff_key($symbolNamePairs, self::$denylist); return $symbolNamePairs; } diff --git a/src/Symfony/Component/Intl/Data/Generator/LanguageDataGenerator.php b/src/Symfony/Component/Intl/Data/Generator/LanguageDataGenerator.php index 79328afcfa..9546227b85 100644 --- a/src/Symfony/Component/Intl/Data/Generator/LanguageDataGenerator.php +++ b/src/Symfony/Component/Intl/Data/Generator/LanguageDataGenerator.php @@ -83,7 +83,7 @@ class LanguageDataGenerator extends AbstractDataGenerator 'za' => 'zha', 'zh' => 'zho', ]; - private static $blacklist = [ + private static $denylist = [ 'root' => true, // Absolute root language 'mul' => true, // Multiple languages 'mis' => true, // Uncoded language @@ -182,7 +182,7 @@ class LanguageDataGenerator extends AbstractDataGenerator private static function generateLanguageNames(ArrayAccessibleResourceBundle $localeBundle): array { - return array_diff_key(iterator_to_array($localeBundle['Languages']), self::$blacklist); + return array_diff_key(iterator_to_array($localeBundle['Languages']), self::$denylist); } private function generateAlpha3Codes(array $languageCodes, ArrayAccessibleResourceBundle $metadataBundle): array diff --git a/src/Symfony/Component/Intl/Data/Generator/RegionDataGenerator.php b/src/Symfony/Component/Intl/Data/Generator/RegionDataGenerator.php index 066cc5fb9d..146475e754 100644 --- a/src/Symfony/Component/Intl/Data/Generator/RegionDataGenerator.php +++ b/src/Symfony/Component/Intl/Data/Generator/RegionDataGenerator.php @@ -40,7 +40,7 @@ class RegionDataGenerator extends AbstractDataGenerator 'YE' => 'YEM', ]; - private static $blacklist = [ + private static $denylist = [ // Exceptional reservations 'AC' => true, // Ascension Island 'CP' => true, // Clipperton Island @@ -69,7 +69,7 @@ class RegionDataGenerator extends AbstractDataGenerator public static function isValidCountryCode($region) { - if (isset(self::$blacklist[$region])) { + if (isset(self::$denylist[$region])) { return false; } diff --git a/src/Symfony/Component/Intl/Data/Generator/ScriptDataGenerator.php b/src/Symfony/Component/Intl/Data/Generator/ScriptDataGenerator.php index 9c26284507..be95b4b9fc 100644 --- a/src/Symfony/Component/Intl/Data/Generator/ScriptDataGenerator.php +++ b/src/Symfony/Component/Intl/Data/Generator/ScriptDataGenerator.php @@ -24,7 +24,7 @@ use Symfony\Component\Intl\Data\Util\LocaleScanner; */ class ScriptDataGenerator extends AbstractDataGenerator { - private static $blacklist = [ + private static $denylist = [ 'Zzzz' => true, // Unknown Script ]; @@ -69,7 +69,7 @@ class ScriptDataGenerator extends AbstractDataGenerator // isset() on \ResourceBundle returns true even if the value is null if (isset($localeBundle['Scripts']) && null !== $localeBundle['Scripts']) { $data = [ - 'Names' => array_diff_key(iterator_to_array($localeBundle['Scripts']), self::$blacklist), + 'Names' => array_diff_key(iterator_to_array($localeBundle['Scripts']), self::$denylist), ]; $this->scriptCodes = array_merge($this->scriptCodes, array_keys($data['Names'])); diff --git a/src/Symfony/Component/Lock/Store/PdoStore.php b/src/Symfony/Component/Lock/Store/PdoStore.php index fb44803681..6470e25a30 100644 --- a/src/Symfony/Component/Lock/Store/PdoStore.php +++ b/src/Symfony/Component/Lock/Store/PdoStore.php @@ -318,26 +318,31 @@ class PdoStore implements PersistingStoreInterface if ($con instanceof \PDO) { $this->driver = $con->getAttribute(\PDO::ATTR_DRIVER_NAME); } else { - switch ($this->driver = $con->getDriver()->getName()) { - case 'mysqli': - throw new NotSupportedException(sprintf('The store "%s" does not support the mysqli driver, use pdo_mysql instead.', static::class)); - case 'pdo_mysql': - case 'drizzle_pdo_mysql': + $driver = $con->getDriver(); + + switch (true) { + case $driver instanceof \Doctrine\DBAL\Driver\Mysqli\Driver: + throw new \LogicException(sprintf('The adapter "%s" does not support the mysqli driver, use pdo_mysql instead.', static::class)); + + case $driver instanceof \Doctrine\DBAL\Driver\AbstractMySQLDriver: $this->driver = 'mysql'; break; - case 'pdo_sqlite': + case $driver instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver: $this->driver = 'sqlite'; break; - case 'pdo_pgsql': + case $driver instanceof \Doctrine\DBAL\Driver\PDOPgSql\Driver: $this->driver = 'pgsql'; break; - case 'oci8': - case 'pdo_oracle': + case $driver instanceof \Doctrine\DBAL\Driver\OCI8\Driver: + case $driver instanceof \Doctrine\DBAL\Driver\PDOOracle\Driver: $this->driver = 'oci'; break; - case 'pdo_sqlsrv': + case $driver instanceof \Doctrine\DBAL\Driver\SQLSrv\Driver: $this->driver = 'sqlsrv'; break; + default: + $this->driver = \get_class($driver); + break; } } diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/ConnectionTest.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/ConnectionTest.php index 88a68defee..5c7d7c7616 100644 --- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/ConnectionTest.php +++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/ConnectionTest.php @@ -210,7 +210,7 @@ class ConnectionTest extends TestCase 'expectedAutoSetup' => false, ]; - yield 'options from options array wins over options from dsn' => [ + yield 'options from dsn array wins over options from options' => [ 'dsn' => 'doctrine://default?table_name=name_from_dsn&redeliver_timeout=1200&queue_name=normal&auto_setup=true', 'options' => [ 'table_name' => 'name_from_options', @@ -219,10 +219,10 @@ class ConnectionTest extends TestCase 'auto_setup' => false, ], 'expectedConnection' => 'default', - 'expectedTableName' => 'name_from_options', - 'expectedRedeliverTimeout' => 1800, - 'expectedQueue' => 'important', - 'expectedAutoSetup' => false, + 'expectedTableName' => 'name_from_dsn', + 'expectedRedeliverTimeout' => 1200, + 'expectedQueue' => 'normal', + 'expectedAutoSetup' => true, ]; yield 'options from dsn with falsey boolean' => [ diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php index 39398519bb..f122530efa 100644 --- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php +++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php @@ -96,7 +96,7 @@ class Connection implements ResetInterface } $configuration = ['connection' => $components['host']]; - $configuration += $options + $query + static::DEFAULT_OPTIONS; + $configuration += $query + $options + static::DEFAULT_OPTIONS; $configuration['auto_setup'] = filter_var($configuration['auto_setup'], FILTER_VALIDATE_BOOLEAN); diff --git a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php index 116977a3c5..58d48b6907 100644 --- a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php +++ b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php @@ -228,11 +228,24 @@ class MessengerPass implements CompilerPassInterface throw new RuntimeException(sprintf('Invalid handler service "%s": argument "$%s" of method "%s::__invoke()" must have a type-hint corresponding to the message class it handles.', $serviceId, $parameters[0]->getName(), $handlerClass->getName())); } + if ($type instanceof \ReflectionUnionType) { + $types = []; + foreach ($type->getTypes() as $type) { + if (!$type->isBuiltin()) { + $types[] = (string) $type; + } + } + + if ($types) { + return $types; + } + } + if ($type->isBuiltin()) { throw new RuntimeException(sprintf('Invalid handler service "%s": type-hint of argument "$%s" in method "%s::__invoke()" must be a class , "%s" given.', $serviceId, $parameters[0]->getName(), $handlerClass->getName(), $type instanceof \ReflectionNamedType ? $type->getName() : (string) $type)); } - return [$parameters[0]->getType()->getName()]; + return [$type->getName()]; } private function registerReceivers(ContainerBuilder $container, array $busIds) diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index 2a484cdc32..aa9bf7dd28 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -206,7 +206,7 @@ class OptionsResolver implements Options return $this; } - if (isset($params[0]) && null !== ($type = $params[0]->getType()) && self::class === $type->getName() && (!isset($params[1]) || (null !== ($type = $params[1]->getType()) && Options::class === $type->getName()))) { + if (isset($params[0]) && null !== ($type = $params[0]->getType()) && self::class === $type->getName() && (!isset($params[1]) || (($type = $params[1]->getType()) instanceof \ReflectionNamedType && Options::class === $type->getName()))) { // Store closure for later evaluation $this->nested[$option][] = $value; $this->defaults[$option] = []; @@ -1283,7 +1283,7 @@ class OptionsResolver implements Options private function getParameterClassName(\ReflectionParameter $parameter): ?string { - if (!($type = $parameter->getType()) || $type->isBuiltin()) { + if (!($type = $parameter->getType()) instanceof \ReflectionNamedType || $type->isBuiltin()) { return null; } diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index f5e64651ba..c7aec338b5 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -428,8 +428,9 @@ class PropertyAccessor implements PropertyAccessorInterface // handle uninitialized properties in PHP >= 7.4 if (\PHP_VERSION_ID >= 70400 && preg_match('/^Typed property ([\w\\\]+)::\$(\w+) must not be accessed before initialization$/', $e->getMessage(), $matches)) { $r = new \ReflectionProperty($matches[1], $matches[2]); + $type= ($type = $r->getType()) instanceof \ReflectionNamedType ? $type->getName() : (string) $type; - throw new UninitializedPropertyException(sprintf('The property "%s::$%s" is not readable because it is typed "%s". You should initialize it or declare a default value instead.', $r->getDeclaringClass()->getName(), $r->getName(), $r->getType()->getName()), 0, $e); + throw new UninitializedPropertyException(sprintf('The property "%s::$%s" is not readable because it is typed "%s". You should initialize it or declare a default value instead.', $r->getDeclaringClass()->getName(), $r->getName(), $type, 0, $e); } throw $e; diff --git a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php index 1cf1534502..29151a5f6e 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php @@ -135,7 +135,7 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp $reflectionProperty = new \ReflectionProperty($class, $property); $type = $reflectionProperty->getType(); if (null !== $type) { - return [$this->extractFromReflectionType($type, $reflectionProperty->getDeclaringClass())]; + return $this->extractFromReflectionType($type, $reflectionProperty->getDeclaringClass()); } } catch (\ReflectionException $e) { // noop @@ -393,11 +393,11 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp } $type = $this->extractFromReflectionType($reflectionType, $reflectionMethod->getDeclaringClass()); - if (\in_array($prefix, $this->arrayMutatorPrefixes)) { - $type = new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $type); + if (1 === \count($type) && \in_array($prefix, $this->arrayMutatorPrefixes)) { + $type = [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $type[0])]; } - return [$type]; + return $type; } /** @@ -413,7 +413,7 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp } if ($reflectionType = $reflectionMethod->getReturnType()) { - return [$this->extractFromReflectionType($reflectionType, $reflectionMethod->getDeclaringClass())]; + return $this->extractFromReflectionType($reflectionType, $reflectionMethod->getDeclaringClass()); } if (\in_array($prefix, ['is', 'can', 'has'])) { @@ -448,7 +448,7 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp } $reflectionType = $parameter->getType(); - return $reflectionType ? [$this->extractFromReflectionType($reflectionType, $constructor->getDeclaringClass())] : null; + return $reflectionType ? $this->extractFromReflectionType($reflectionType, $constructor->getDeclaringClass()) : null; } if ($parentClass = $reflectionClass->getParentClass()) { @@ -477,22 +477,26 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp return [new Type(static::MAP_TYPES[$type] ?? $type)]; } - private function extractFromReflectionType(\ReflectionType $reflectionType, \ReflectionClass $declaringClass): Type + private function extractFromReflectionType(\ReflectionType $reflectionType, \ReflectionClass $declaringClass): array { - $phpTypeOrClass = $reflectionType->getName(); + $types = []; $nullable = $reflectionType->allowsNull(); - if (Type::BUILTIN_TYPE_ARRAY === $phpTypeOrClass) { - $type = new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true); - } elseif ('void' === $phpTypeOrClass) { - $type = new Type(Type::BUILTIN_TYPE_NULL, $nullable); - } elseif ($reflectionType->isBuiltin()) { - $type = new Type($phpTypeOrClass, $nullable); - } else { - $type = new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $this->resolveTypeName($phpTypeOrClass, $declaringClass)); + foreach ($reflectionType instanceof \ReflectionUnionType ? $reflectionType->getTypes() : [$reflectionType] as $type) { + $phpTypeOrClass = $reflectionType instanceof \ReflectionNamedType ? $reflectionType->getName() : (string) $type; + + if (Type::BUILTIN_TYPE_ARRAY === $phpTypeOrClass) { + $types[] = new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true); + } elseif ('void' === $phpTypeOrClass || 'null' === $phpTypeOrClass) { + $types[] = new Type(Type::BUILTIN_TYPE_NULL, $nullable); + } elseif ($reflectionType->isBuiltin()) { + $types[] = new Type($phpTypeOrClass, $nullable); + } else { + $types[] = new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $this->resolveTypeName($phpTypeOrClass, $declaringClass)); + } } - return $type; + return $types; } private function resolveTypeName(string $name, \ReflectionClass $declaringClass): string diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/CompiledUrlMatcherDumper.php b/src/Symfony/Component/Routing/Matcher/Dumper/CompiledUrlMatcherDumper.php index 936dd16ad1..73e2e1e0a0 100644 --- a/src/Symfony/Component/Routing/Matcher/Dumper/CompiledUrlMatcherDumper.php +++ b/src/Symfony/Component/Routing/Matcher/Dumper/CompiledUrlMatcherDumper.php @@ -244,7 +244,7 @@ EOF; * Last but not least: * - Because it is not possibe to mix unicode/non-unicode patterns in a single regexp, several of them can be generated. * - The same regexp can be used several times when the logic in the switch rejects the match. When this happens, the - * matching-but-failing subpattern is blacklisted by replacing its name by "(*F)", which forces a failure-to-match. + * matching-but-failing subpattern is excluded by replacing its name by "(*F)", which forces a failure-to-match. * To ease this backlisting operation, the name of subpatterns is also the string offset where the replacement should occur. */ private function compileDynamicRoutes(RouteCollection $collection, bool $matchHost, int $chunkLimit, array &$conditions): array diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index e0b52da867..b957010bb0 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -411,7 +411,7 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn protected function denormalizeParameter(\ReflectionClass $class, \ReflectionParameter $parameter, string $parameterName, $parameterData, array $context, string $format = null) { try { - if (($parameterType = $parameter->getType()) && !$parameterType->isBuiltin()) { + if (($parameterType = $parameter->getType()) instanceof \ReflectionNamedType && !$parameterType->isBuiltin()) { $parameterClass = $parameterType->getName(); new \ReflectionClass($parameterClass); // throws a \ReflectionException if the class doesn't exist diff --git a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php index 25da26f4b8..470229b5b5 100644 --- a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php @@ -97,7 +97,7 @@ class ReflectionCaster $prefix = Caster::PREFIX_VIRTUAL; $a += [ - $prefix.'name' => $c->getName(), + $prefix.'name' => $c instanceof \ReflectionNamedType ? $c->getName() : (string) $c, $prefix.'allowsNull' => $c->allowsNull(), $prefix.'isBuiltin' => $c->isBuiltin(), ]; @@ -182,7 +182,7 @@ class ReflectionCaster if (isset($a[$prefix.'returnType'])) { $v = $a[$prefix.'returnType']; - $v = $v->getName(); + $v = $v instanceof \ReflectionNamedType ? $v->getName() : (string) $v; $a[$prefix.'returnType'] = new ClassStub($a[$prefix.'returnType']->allowsNull() ? '?'.$v : $v, [class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', '']); } if (isset($a[$prefix.'class'])) { @@ -244,7 +244,7 @@ class ReflectionCaster ]); if ($v = $c->getType()) { - $a[$prefix.'typeHint'] = $v->getName(); + $a[$prefix.'typeHint'] = $v instanceof \ReflectionNamedType ? $v->getName() : (string) $v; } if (isset($a[$prefix.'typeHint'])) { @@ -320,10 +320,14 @@ class ReflectionCaster foreach ($a[$prefix.'parameters']->value as $k => $param) { $signature .= ', '; if ($type = $param->getType()) { - if (!$param->isOptional() && $param->allowsNull()) { - $signature .= '?'; + if (!$type instanceof \ReflectionNamedType) { + $signature .= $type.' '; + } else { + if (!$param->isOptional() && $param->allowsNull()) { + $signature .= '?'; + } + $signature .= substr(strrchr('\\'.$type->getName(), '\\'), 1).' '; } - $signature .= substr(strrchr('\\'.$type->getName(), '\\'), 1).' '; } $signature .= $k; diff --git a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php index a840429375..8ad86c73f8 100644 --- a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php @@ -283,7 +283,8 @@ class CliDumper extends AbstractDumper } elseif (Cursor::HASH_RESOURCE === $type) { $prefix = $this->style('note', $class.' resource', $attr).($hasChild ? ' {' : ' '); } else { - $prefix = $class && !(self::DUMP_LIGHT_ARRAY & $this->flags) ? $this->style('note', 'array:'.$class, $attr).' [' : '['; + $unstyledPrefix = $class && !(self::DUMP_LIGHT_ARRAY & $this->flags) ? 'array:'.$class : ''; + $prefix = $this->style('note', $unstyledPrefix, $attr).($unstyledPrefix ? ' [' : '['); } if (($cursor->softRefCount || 0 < $cursor->softRefHandle) && empty($attr['cut_hash'])) { diff --git a/src/Symfony/Component/VarDumper/Tests/Dumper/CliDumperTest.php b/src/Symfony/Component/VarDumper/Tests/Dumper/CliDumperTest.php index a856fa04fb..a23b94497a 100644 --- a/src/Symfony/Component/VarDumper/Tests/Dumper/CliDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Dumper/CliDumperTest.php @@ -13,6 +13,7 @@ namespace Symfony\Component\VarDumper\Tests\Dumper; use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Cloner\VarCloner; +use Symfony\Component\VarDumper\Dumper\AbstractDumper; use Symfony\Component\VarDumper\Dumper\CliDumper; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; use Twig\Environment; @@ -489,6 +490,57 @@ EOTXT ); } + public function provideDumpArrayWithColor() + { + yield [ + ['foo' => 'bar'], + 0, +<< "\e[1;38;5;113mbar\e[0;38;5;208m"\e[m +\e[0;38;5;208m]\e[m + +EOTXT + ]; + + yield [[], AbstractDumper::DUMP_LIGHT_ARRAY, "\e[0;38;5;208m\e[38;5;38m\e[0;38;5;208m[]\e[m\n"]; + + yield [ + ['foo' => 'bar'], + AbstractDumper::DUMP_LIGHT_ARRAY, + << "\e[1;38;5;113mbar\e[0;38;5;208m"\e[m +\e[0;38;5;208m]\e[m + +EOTXT + ]; + + yield [[], 0, "\e[0;38;5;208m\e[38;5;38m\e[0;38;5;208m[]\e[m\n"]; + } + + /** + * @dataProvider provideDumpArrayWithColor + */ + public function testDumpArrayWithColor($value, $flags, $expectedOut) + { + if ('\\' === \DIRECTORY_SEPARATOR) { + $this->markTestSkipped('Windows console does not support coloration'); + } + + $out = ''; + $dumper = new CliDumper(function ($line, $depth) use (&$out) { + if ($depth >= 0) { + $out .= str_repeat(' ', $depth).$line."\n"; + } + }, null, $flags); + $dumper->setColors(true); + $cloner = new VarCloner(); + $dumper->dump($cloner->cloneVar($value)); + + $this->assertSame($expectedOut, $out); + } + private function getSpecialVars() { foreach (array_keys($GLOBALS) as $var) { diff --git a/src/Symfony/Contracts/Service/ServiceLocatorTrait.php b/src/Symfony/Contracts/Service/ServiceLocatorTrait.php index 0b4d60affa..1737f50e99 100644 --- a/src/Symfony/Contracts/Service/ServiceLocatorTrait.php +++ b/src/Symfony/Contracts/Service/ServiceLocatorTrait.php @@ -87,7 +87,7 @@ trait ServiceLocatorTrait } else { $type = (new \ReflectionFunction($factory))->getReturnType(); - $this->providedTypes[$name] = $type ? ($type->allowsNull() ? '?' : '').$type->getName() : '?'; + $this->providedTypes[$name] = $type ? ($type->allowsNull() ? '?' : '').($type instanceof \ReflectionNamedType ? $type->getName() : $type) : '?'; } } } diff --git a/src/Symfony/Contracts/Service/ServiceSubscriberTrait.php b/src/Symfony/Contracts/Service/ServiceSubscriberTrait.php index 5d9d456d0c..82fb5ab361 100644 --- a/src/Symfony/Contracts/Service/ServiceSubscriberTrait.php +++ b/src/Symfony/Contracts/Service/ServiceSubscriberTrait.php @@ -40,7 +40,7 @@ trait ServiceSubscriberTrait } if (self::class === $method->getDeclaringClass()->name && ($returnType = $method->getReturnType()) && !$returnType->isBuiltin()) { - $services[self::class.'::'.$method->name] = '?'.$returnType->getName(); + $services[self::class.'::'.$method->name] = '?'.($returnType instanceof \ReflectionNamedType ? $returnType->getName() : $type); } }