diff --git a/.github/expected-missing-return-types.diff b/.github/expected-missing-return-types.diff index 39683c93b38ba..bee14f3b0b283 100644 --- a/.github/expected-missing-return-types.diff +++ b/.github/expected-missing-return-types.diff @@ -6,6 +6,24 @@ head=$(sed '/^diff /Q' .github/expected-missing-return-types.diff) (echo "$head" && echo && git diff -U2 src/) > .github/expected-missing-return-types.diff git checkout composer.json src/ +diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php +index f52a1ce1e9..1fb72535e1 100644 +--- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php ++++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php +@@ -285,5 +285,5 @@ class QueryMock extends AbstractQuery + * @return array|string + */ +- public function getSQL() ++ public function getSQL(): array|string + { + } +@@ -292,5 +292,5 @@ class QueryMock extends AbstractQuery + * @return Result|int + */ +- protected function _doExecute() ++ protected function _doExecute(): Result|int + { + } diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php index bb5560a7b5..be86cbf98e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php @@ -167,7 +185,7 @@ index 6b1c6c5fbe..bb80ed461e 100644 + public function isFresh(ResourceInterface $resource, int $timestamp): bool; } diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php -index d4ec1be090..747e093b1b 100644 +index 41548a3e9d..f99739454f 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -215,5 +215,5 @@ class Application implements ResetInterface @@ -276,7 +294,7 @@ index 2762cdf05c..737334268a 100644 + public function getName(): string; } diff --git a/src/Symfony/Component/Console/Helper/Table.php b/src/Symfony/Component/Console/Helper/Table.php -index 893b3192e9..94822ed191 100644 +index 907c9f505c..3bfd23061a 100644 --- a/src/Symfony/Component/Console/Helper/Table.php +++ b/src/Symfony/Component/Console/Helper/Table.php @@ -193,5 +193,5 @@ class Table @@ -322,6 +340,17 @@ index 08bab02ee4..1181f0795e 100644 + protected function processValue(mixed $value, bool $isRoot = false): mixed { if (\is_array($value)) { +diff --git a/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php b/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php +index 895c155e07..3955c902d4 100644 +--- a/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php ++++ b/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php +@@ -33,5 +33,5 @@ class DefinitionErrorExceptionPass extends AbstractRecursivePass + * @return void + */ +- public function process(ContainerBuilder $container) ++ public function process(ContainerBuilder $container): void + { + try { diff --git a/src/Symfony/Component/DependencyInjection/Container.php b/src/Symfony/Component/DependencyInjection/Container.php index f5d33682ff..e644489097 100644 --- a/src/Symfony/Component/DependencyInjection/Container.php @@ -595,10 +624,10 @@ index 1cb865fd66..f6f4efe7a7 100644 + public function getName(): string; } diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php -index f77a99e0ff..a6829efa75 100644 +index fb84d96428..8ab979360a 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php -@@ -457,5 +457,5 @@ class HttpCache implements HttpKernelInterface, TerminableInterface +@@ -459,5 +459,5 @@ class HttpCache implements HttpKernelInterface, TerminableInterface * @return Response */ - protected function forward(Request $request, bool $catch = false, Response $entry = null) @@ -922,7 +951,7 @@ index 84a84ad1f3..6f66b6d32a 100644 + public function supportsDecoding(string $format): bool; } diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php -index 829e178407..1ac8101771 100644 +index e2b6c1bc8d..3c742d8cab 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -210,5 +210,5 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn @@ -947,7 +976,7 @@ index 829e178407..1ac8101771 100644 { if (null !== $object = $this->extractObjectToPopulate($class, $context, self::OBJECT_TO_POPULATE)) { diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php -index 7c4c5fb41b..840dd2bcdb 100644 +index 98ccfca4e4..0b1bc69b00 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -139,10 +139,10 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer @@ -969,15 +998,15 @@ index 7c4c5fb41b..840dd2bcdb 100644 - protected function instantiateObject(array &$data, string $class, array &$context, \ReflectionClass $reflectionClass, array|bool $allowedAttributes, string $format = null) + protected function instantiateObject(array &$data, string $class, array &$context, \ReflectionClass $reflectionClass, array|bool $allowedAttributes, string $format = null): object { - if ($this->classDiscriminatorResolver && $mapping = $this->classDiscriminatorResolver->getMappingForClass($class)) { -@@ -287,5 +287,5 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer + if ($class !== $mappedClass = $this->getMappedClass($data, $class, $context)) { +@@ -276,5 +276,5 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer * @return string[] */ - abstract protected function extractAttributes(object $object, string $format = null, array $context = []); + abstract protected function extractAttributes(object $object, string $format = null, array $context = []): array; /** -@@ -294,15 +294,15 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer +@@ -283,15 +283,15 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer * @return mixed */ - abstract protected function getAttributeValue(object $object, string $attribute, string $format = null, array $context = []); @@ -997,7 +1026,7 @@ index 7c4c5fb41b..840dd2bcdb 100644 { if (!isset($context['cache_key'])) { diff --git a/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php b/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php -index ae3adbfe33..3a38429cf1 100644 +index 1786d6fff1..04a2e62ed2 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php @@ -45,5 +45,5 @@ interface DenormalizerInterface @@ -1014,7 +1043,7 @@ index ae3adbfe33..3a38429cf1 100644 + public function supportsDenormalization(mixed $data, string $type, string $format = null /* , array $context = [] */): bool; } diff --git a/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php b/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php -index 691e9c70f0..fc87f672e1 100644 +index cb43d78cc7..d215ffe997 100644 --- a/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php @@ -37,5 +37,5 @@ interface NormalizerInterface @@ -1090,10 +1119,10 @@ index ee1d68c78f..9baaabb04c 100644 { return self::PROPERTY_CONSTRAINT; diff --git a/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php b/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php -index 9aa01a9dec..c18b07ac46 100644 +index 744a2cd720..b656f2885f 100644 --- a/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php +++ b/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php -@@ -307,5 +307,5 @@ abstract class ConstraintValidatorTestCase extends TestCase +@@ -303,5 +303,5 @@ abstract class ConstraintValidatorTestCase extends TestCase * @psalm-return T */ - abstract protected function createValidator(); diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 88ba6a12af053..cf7eca61aaa21 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -28,7 +28,7 @@ jobs: services: postgres: - image: postgres:9.6-alpine + image: postgres:10.6-alpine ports: - 5432:5432 env: diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 86206dae8d6ad..2f57845559044 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -27,11 +27,11 @@ jobs: matrix: include: - php: '8.1' - - php: '8.1' + - php: '8.2' mode: high-deps - - php: '8.1' - mode: low-deps - php: '8.2' + mode: low-deps + - php: '8.3' #mode: experimental fail-fast: false @@ -64,7 +64,7 @@ jobs: echo COLUMNS=120 >> $GITHUB_ENV echo PHPUNIT="$(pwd)/phpunit --exclude-group tty,benchmark,intl-data,integration" >> $GITHUB_ENV - echo COMPOSER_UP='composer update --no-progress --ansi'$([[ "${{ matrix.mode }}" = experimental ]] && echo ' --ignore-platform-req=php+') >> $GITHUB_ENV + echo COMPOSER_UP='composer update --no-progress --ansi'$([[ "${{ matrix.mode }}" != low-deps ]] && echo ' --ignore-platform-req=php+') >> $GITHUB_ENV SYMFONY_VERSIONS=$(git ls-remote -q --heads | cut -f2 | grep -o '/[1-9][0-9]*\.[0-9].*' | sort -V) SYMFONY_VERSION=$(grep ' VERSION = ' src/Symfony/Component/HttpKernel/Kernel.php | cut -d "'" -f2 | cut -d '.' -f 1-2) diff --git a/CHANGELOG-6.2.md b/CHANGELOG-6.2.md index 5a003a6343fe2..1be744e9c26c5 100644 --- a/CHANGELOG-6.2.md +++ b/CHANGELOG-6.2.md @@ -7,6 +7,35 @@ in 6.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/v6.2.0...v6.2.1 +* 6.2.12 (2023-06-26) + + * bug #50763 [DependencyInjection] Skip errored definitions deep-referenced as runtime exceptions (nicolas-grekas) + * bug #50728 [HttpClient] Explicitly exclude CURLOPT_POSTREDIR (nicolas-grekas) + * bug #48961 [WebProfilerBundle] right blocks: fix display (jmsche) + * bug #50671 [HttpClient] Fix encoding some characters in query strings (Daniel Kozák) + * bug #50655 Revert "Respect isRetryable decision of the retry strategy for re-delivery" (bendavies) + * bug #50665 [FrameworkBundle] Ignore missing directories in about command (ro0NL) + * bug #50644 [VarDumper] Dumping DateTime throws error if getTimezone is false (bogdanmoza) + * bug #50656 Only update autoload_runtime.php when it changed (Seldaek) + * bug #50698 [HttpClient] Fix int conversion for `GenericRetryStrategy` with floated multiplier (francisbesset) + * bug #50611 [Clock] Fix MockClock::modify() on PHP 8.3 (nicolas-grekas) + * bug #50548 [FrameworkBundle] Show non-bundle extensions in `debug:config` & `config:dump` list view & completion (HypeMC) + * bug #50585 [Cache] Fix RedisTrait::createConnection for cluster (darkanakin41) + * bug #50546 [FrameworkBundle] Fix `debug:config` & `config:dump` in debug mode (HypeMC) + * bug #50560 [DependencyInjection] Support PHP 8.2 `true` and `null` type (ruudk) + * bug #50562 [Lock] Fix sprintf (fancyweb) + * bug #50524 Fix Doctrine deprecations (nicolas-grekas) + * bug #50539 [Validator] Remove internal from methods on non-internal interfaces (wouterj) + * bug #50534 [PhpUnitBridge] Fix support for the NO_COLOR env var (nicolas-grekas) + * bug #50517 [DependencyInjection] Fix casting scalar env vars from null (fancyweb) + * bug #50515 [Mailer] [MailPace] Fix undefined array key in errors response (Florian Heller) + * bug #50507 [Cache] Fix DBAL deprecations (MatTheCat) + * bug #50501 [Serializer] Fix discriminator map not working with `AbstractNormalizer::OBJECT_TO_POPULATE` (HypeMC) + * bug #50480 [Serializer] Fix discriminator map not working with `AbstractNormalizer::OBJECT_TO_POPULATE` (HypeMC) + * bug #50437 [Filesystem] Follow symlinks when dumping files (ausi) + * bug #50478 [DependencyInjection] Escape `` from parameter-like default values (MatTheCat) + * bug #50452 Hide definitions bearing the `container.excluded` tag (Myks92) + * 6.2.11 (2023-05-27) * bug #50442 [SecurityBundle] Update security-1.0.xsd to include missing access-token definition (aegypius) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 24afd64907140..cf5f960f7a101 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -21,9 +21,9 @@ The Symfony Connect username in parenthesis allows to get more information - Jordi Boggiano (seldaek) - Roland Franssen (ro0) - Victor Berchet (victor) + - Javier Eguiluz (javier.eguiluz) - Yonel Ceruto (yonelceruto) - Tobias Nyholm (tobias) - - Javier Eguiluz (javier.eguiluz) - Oskar Stark (oskarstark) - Ryan Weaver (weaverryan) - Johannes S (johannes) @@ -40,8 +40,8 @@ The Symfony Connect username in parenthesis allows to get more information - Abdellatif Ait boudad (aitboudad) - Jan Schädlich (jschaedl) - Lukas Kahwe Smith (lsmith) - - Jérôme Tamarelle (gromnan) - Kevin Bond (kbond) + - Jérôme Tamarelle (gromnan) - Martin Hasoň (hason) - Jeremy Mikola (jmikola) - Jean-François Simon (jfsimon) @@ -52,21 +52,21 @@ The Symfony Connect username in parenthesis allows to get more information - Valentin Udaltsov (vudaltsov) - Vasilij Duško (staff) - Matthias Pigulla (mpdude) + - Antoine Lamirault (alamirault) - Gabriel Ostrolucký (gadelat) - - Antoine Makdessi (amakdessi) - Laurent VOULLEMIER (lvo) + - Antoine Makdessi (amakdessi) - Pierre du Plessis (pierredup) - - Antoine Lamirault (alamirault) - Grégoire Paris (greg0ire) - Jonathan Wage (jwage) + - Mathieu Lechat (mat_the_cat) - Titouan Galopin (tgalopin) - David Maicher (dmaicher) + - Alexander Schranz (alexander-schranz) - Gábor Egyed (1ed) - Mathieu Santostefano (welcomattic) - - Alexander Schranz (alexander-schranz) - Alexandre Salomé (alexandresalome) - William DURAND - - Mathieu Lechat (mat_the_cat) - ornicar - Dany Maillard (maidmaid) - Eriksen Costa @@ -76,10 +76,10 @@ The Symfony Connect username in parenthesis allows to get more information - Francis Besset (francisbesset) - Vasilij Dusko | CREATION - Bulat Shakirzyanov (avalanche123) + - Vincent Langlet (deviling) - Iltar van der Berg - Miha Vrhovnik (mvrhov) - Mathieu Piot (mpiot) - - Vincent Langlet (deviling) - Saša Stamenković (umpirsky) - Alex Pott - Guilhem N (guilhemn) @@ -95,14 +95,14 @@ The Symfony Connect username in parenthesis allows to get more information - Ruud Kamphuis (ruudk) - Henrik Bjørnskov (henrikbjorn) - David Buchmann (dbu) + - Massimiliano Arione (garak) - Andrej Hudec (pulzarraider) - Julien Falque (julienfalque) - - Massimiliano Arione (garak) - Jáchym Toušek (enumag) - Douglas Greenshields (shieldo) + - Mathias Arlaud (mtarld) - Christian Raue - Fran Moreno (franmomu) - - Mathias Arlaud (mtarld) - Graham Campbell (graham) - Michel Weimerskirch (mweimerskirch) - Eric Clemmons (ericclemmons) @@ -117,18 +117,18 @@ The Symfony Connect username in parenthesis allows to get more information - Dariusz Górecki (canni) - Maxime Helias (maxhelias) - Ener-Getick - - Sebastiaan Stok (sstok) - Tugdual Saunier (tucksaun) + - Sebastiaan Stok (sstok) - Jérôme Vasseur (jvasseur) - Ion Bazan (ionbazan) - Rokas Mikalkėnas (rokasm) + - Yanick Witschi (toflar) - Lee McDermott - Brandon Turner - Luis Cordova (cordoval) - Daniel Holmes (dholmes) - Toni Uebernickel (havvg) - Bart van den Burg (burgov) - - Yanick Witschi (toflar) - Jordan Alliot (jalliot) - Smaine Milianni (ismail1432) - John Wards (johnwards) @@ -137,6 +137,7 @@ The Symfony Connect username in parenthesis allows to get more information - Antoine Hérault (herzult) - Konstantin.Myakshin - Arman Hosseini (arman) + - gnito-org - Saif Eddin Gmati (azjezz) - Simon Berger - Arnaud Le Blanc (arnaud-lb) @@ -152,6 +153,7 @@ The Symfony Connect username in parenthesis allows to get more information - Chris Wilkinson (thewilkybarkid) - Brice BERNARD (brikou) - Roman Martinuk (a2a4) + - Joel Wurtz (brouznouf) - Gregor Harlan (gharlan) - Baptiste Clavié (talus) - Adrien Brault (adrienbrault) @@ -168,7 +170,6 @@ The Symfony Connect username in parenthesis allows to get more information - Guillaume (guill) - Christopher Hertel (chertel) - Jacob Dreesen (jdreesen) - - Joel Wurtz (brouznouf) - Olivier Dolbeau (odolbeau) - Florian Voutzinos (florianv) - zairig imad (zairigimad) @@ -180,6 +181,7 @@ The Symfony Connect username in parenthesis allows to get more information - HeahDude - Richard van Laak (rvanlaak) - Paráda József (paradajozsef) + - Hubert Lenoir (hubert_lenoir) - Alessandro Lai (jean85) - Alexander Schwenn (xelaris) - Fabien Pennequin (fabienpennequin) @@ -203,7 +205,6 @@ The Symfony Connect username in parenthesis allows to get more information - Tigran Azatyan (tigranazatyan) - Eric GELOEN (gelo) - Matthieu Napoli (mnapoli) - - Hubert Lenoir (hubert_lenoir) - Tomáš Votruba (tomas_votruba) - Joshua Thijssen - Stefano Sala (stefano.sala) @@ -295,7 +296,6 @@ The Symfony Connect username in parenthesis allows to get more information - Samuel NELA (snela) - Romain Monteil (ker0x) - dFayet - - gnito-org - Karoly Gossler (connorhu) - Vincent AUBERT (vincent) - Sebastien Morel (plopix) @@ -304,7 +304,9 @@ The Symfony Connect username in parenthesis allows to get more information - Timothée Barray (tyx) - Sébastien Alfaiate (seb33300) - James Halsall (jaitsu) + - Maximilian Beckers (maxbeckers) - Mikael Pajunen + - Marcin Sikoń (marphi) - Warnar Boekkooi (boekkooi) - Marco Petersen (ocrampete16) - Benjamin Leveque (benji07) @@ -335,6 +337,7 @@ The Symfony Connect username in parenthesis allows to get more information - Urinbayev Shakhobiddin (shokhaa) - Ahmed Raafat - Philippe Segatori + - Allison Guilhem (a_guilhem) - Thibaut Cheymol (tcheymol) - Julien Pauli - Islam Israfilov (islam93) @@ -346,7 +349,6 @@ The Symfony Connect username in parenthesis allows to get more information - Ruben Gonzalez (rubenrua) - Benjamin Dulau (dbenjamin) - Pavel Kirpitsov (pavel-kirpichyov) - - Maximilian Beckers (maxbeckers) - Mathieu Lemoine (lemoinem) - Christian Schmidt - Andreas Hucks (meandmymonkey) @@ -357,7 +359,6 @@ The Symfony Connect username in parenthesis allows to get more information - Clara van Miert - Martin Auswöger - Alexander Menshchikov - - Marcin Sikoń (marphi) - Stepan Anchugov (kix) - bronze1man - sun (sun) @@ -375,11 +376,13 @@ The Symfony Connect username in parenthesis allows to get more information - Kyle - Dominique Bongiraud - Hidde Wieringa (hiddewie) + - Dane Powell - Christopher Davis (chrisguitarguy) - Lukáš Holeczy (holicz) - Michael Lee (zerustech) - Florian Lonqueu-Brochard (florianlb) - Leszek Prabucki (l3l0) + - Giorgio Premi - Emanuele Panzeri (thepanz) - Matthew Smeets - François Zaninotto (fzaninotto) @@ -415,14 +418,12 @@ The Symfony Connect username in parenthesis allows to get more information - Mantis Development - Pablo Lozano (arkadis) - quentin neyrat (qneyrat) - - Dane Powell - Antonio Jose Cerezo (ajcerezo) - Marcin Szepczynski (czepol) - Lescot Edouard (idetox) - Loïc Frémont (loic425) - Rob Frawley 2nd (robfrawley) - Mohammad Emran Hasan (phpfour) - - Allison Guilhem (a_guilhem) - Dmitriy Mamontov (mamontovdmitriy) - Kévin THERAGE (kevin_therage) - Nikita Konstantinov (unkind) @@ -430,7 +431,6 @@ The Symfony Connect username in parenthesis allows to get more information - Francois Zaninotto - Laurent Masforné (heisenberg) - Claude Khedhiri (ck-developer) - - Giorgio Premi - Daniel Tschinder - Christian Schmidt - Alexander Kotynia (olden) @@ -576,6 +576,7 @@ The Symfony Connect username in parenthesis allows to get more information - Grégoire Passault (gregwar) - Jerzy Zawadzki (jzawadzki) - Ismael Ambrosi (iambrosi) + - Samaël Villette (samadu61) - Saif Eddin G - Emmanuel BORGES (eborges78) - siganushka (siganushka) @@ -691,6 +692,7 @@ The Symfony Connect username in parenthesis allows to get more information - Ruben Jacobs (rubenj) - Arkadius Stefanski (arkadius) - Jérémy M (th3mouk) + - Tristan Pouliquen - Terje Bråten - Pierre Rineau - Renan Gonçalves (renan_saddam) @@ -720,6 +722,7 @@ The Symfony Connect username in parenthesis allows to get more information - Eric COURTIAL - Xesxen - ShinDarth + - Phil E. Taylor (philetaylor) - Arun Philip - Stéphane PY (steph_py) - Philipp Kräutli (pkraeutli) @@ -752,10 +755,10 @@ The Symfony Connect username in parenthesis allows to get more information - Hassan Amouhzi - Antonin CLAUZIER (0x346e3730) - Andrei C. (moldman) - - Samaël Villette (samadu61) - Tamas Szijarto - stlrnz - Adrien Wilmet (adrienfr) + - Mathieu Rochette (mathroc) - Alex Bacart - hugovms - Michele Locati @@ -898,6 +901,7 @@ The Symfony Connect username in parenthesis allows to get more information - Jonas Flodén (flojon) - Adrien Lucas (adrienlucas) - Dominik Zogg + - Quentin Devos - Kai Dederichs - Luc Vieillescazes (iamluc) - Thomas Nunninger @@ -956,7 +960,6 @@ The Symfony Connect username in parenthesis allows to get more information - Gigino Chianese (sajito) - Xav` (xavismeh) - Remi Collet - - Mathieu Rochette (mathroc) - Vicent Soria Durá (vicentgodella) - Michael Moravec - Anthony Ferrara @@ -1026,6 +1029,7 @@ The Symfony Connect username in parenthesis allows to get more information - Loïc Faugeron - Aurélien Fredouelle - Pavel Campr (pcampr) + - Markus Staab - Forfarle (forfarle) - Johnny Robeson (johnny) - Kai Eichinger (kai_eichinger) @@ -1267,7 +1271,6 @@ The Symfony Connect username in parenthesis allows to get more information - Szijarto Tamas - Arend Hummeling - Makdessi Alex - - Phil E. Taylor (philetaylor) - Juan Miguel Besada Vidal (soutlink) - dlorek - Stuart Fyfe @@ -1295,7 +1298,6 @@ The Symfony Connect username in parenthesis allows to get more information - Simon Schick (simonsimcity) - Victor Macko (victor_m) - Tristan Roussel - - Quentin Devos - Jorge Vahldick (jvahldick) - Vladimir Mantulo (mantulo) - aim8604 @@ -1303,6 +1305,7 @@ The Symfony Connect username in parenthesis allows to get more information - Maciej Zgadzaj - David Legatt (dlegatt) - Maarten de Boer (mdeboer) + - Alexandre parent - Cameron Porter - Hossein Bukhamsin - Oliver Hoff @@ -1449,7 +1452,6 @@ The Symfony Connect username in parenthesis allows to get more information - Michael Olšavský - Benny Born - Emirald Mateli - - Tristan Pouliquen - Jose Gonzalez - Claudio Zizza - Ivo Valchev @@ -1841,7 +1843,6 @@ The Symfony Connect username in parenthesis allows to get more information - vladyslavstartsev - Kévin - Marc Abramowitz - - Markus Staab - michal - Martijn Evers - Sjoerd Adema @@ -2220,11 +2221,13 @@ The Symfony Connect username in parenthesis allows to get more information - Andrew Tch - Alexander Cheprasov - Rodrigo Díez Villamuera (rodrigodiez) + - Brad Treloar - Stephen Clouse - e-ivanov - Abderrahman DAIF (death_maker) - Yann Rabiller (einenlum) - Jochen Bayer (jocl) + - Constantine Shtompel - VAN DER PUTTE Guillaume (guillaume_vdp) - Patrick Carlo-Hickman - Bruno MATEU @@ -2235,6 +2238,7 @@ The Symfony Connect username in parenthesis allows to get more information - Viacheslav Sychov - Nicolas Sauveur (baishu) - Helmut Hummel (helhum) + - Andrew Neil Forster (krciga22) - Matt Brunt - Carlos Ortega Huetos - Péter Buri (burci) @@ -2285,7 +2289,6 @@ The Symfony Connect username in parenthesis allows to get more information - John Espiritu (johnillo) - Oxan van Leeuwen - pkowalczyk - - Alexandre parent - Soner Sayakci - Max Voloshin (maxvoloshin) - Nicolas Fabre (nfabre) @@ -2400,6 +2403,7 @@ The Symfony Connect username in parenthesis allows to get more information - Claudiu Cristea - Zacharias Luiten - Sebastian Utz + - Oliver Hader - Adrien Gallou (agallou) - Maks Rafalko (bornfree) - Conrad Kleinespel (conradk) @@ -2417,6 +2421,7 @@ The Symfony Connect username in parenthesis allows to get more information - Cédric Lahouste (rapotor) - Samuel Vogel (samuelvogel) - Berat Doğan + - Christian Kolb - Guillaume LECERF - Juanmi Rodriguez Cerón - twifty @@ -2437,6 +2442,8 @@ The Symfony Connect username in parenthesis allows to get more information - Eric Stern - ShiraNai7 - Antal Áron (antalaron) + - Alexander Grimalovsky (flying) + - Ivan Pepelko (pepelko) - Vašek Purchart (vasek-purchart) - Janusz Jabłoński (yanoosh) - Fleuv @@ -2581,6 +2588,7 @@ The Symfony Connect username in parenthesis allows to get more information - Nicolas Schwartz (nicoschwartz) - Tim Jabs (rubinum) - Stéphane Seng (stephaneseng) + - Robert Korulczyk - Jonathan Gough - Benoit Leveque - Benjamin Bender @@ -2652,6 +2660,7 @@ The Symfony Connect username in parenthesis allows to get more information - Martin Schophaus (m_schophaus_adcada) - Martynas Sudintas (martiis) - Anton Sukhachev (mrsuh) + - Vitaliy Zhuk (zhukv) - Marcel Siegert - ryunosuke - Roy de Vos Burchart @@ -2707,6 +2716,7 @@ The Symfony Connect username in parenthesis allows to get more information - Jovan Perovic (jperovic) - Pablo Maria Martelletti (pmartelletti) - Sander van der Vlugt (stranding) + - Maxim Tugaev (tugmaks) - Florian Bogey - Waqas Ahmed - Bert Hekman @@ -3141,6 +3151,7 @@ The Symfony Connect username in parenthesis allows to get more information - Antoine LA - Vyacheslav Slinko - Benjamin Laugueux + - Lane Shukhov - Jakub Chábek - William Pinaud (DocFX) - Johannes @@ -3244,6 +3255,7 @@ The Symfony Connect username in parenthesis allows to get more information - Ahmed Abdulrahman - dinitrol - Penny Leach + - Kevin Mian Kraiker - Yurii K - Richard Trebichavský - g123456789l @@ -3282,6 +3294,7 @@ The Symfony Connect username in parenthesis allows to get more information - ADmad - Nicolas Roudaire - Abdouni Karim (abdounikarim) + - Adrian Günter (adrianguenter) - Andreas Forsblom (aforsblo) - Alex Olmos (alexolmos) - Cedric BERTOLINI (alsciende) diff --git a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php index 7381a6a992679..7582add20f907 100644 --- a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php +++ b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php @@ -11,6 +11,7 @@ namespace Symfony\Bridge\Doctrine\Form\ChoiceList; +use Doctrine\DBAL\ArrayParameterType; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Types\ConversionException; use Doctrine\DBAL\Types\Type; @@ -70,7 +71,7 @@ public function getEntitiesByIds(string $identifier, array $values): array $entity = current($qb->getRootEntities()); $metadata = $qb->getEntityManager()->getClassMetadata($entity); if (\in_array($type = $metadata->getTypeOfField($identifier), ['integer', 'bigint', 'smallint'])) { - $parameterType = Connection::PARAM_INT_ARRAY; + $parameterType = class_exists(ArrayParameterType::class) ? ArrayParameterType::INTEGER : Connection::PARAM_INT_ARRAY; // Filter out non-integer values (e.g. ""). If we don't, some // databases such as PostgreSQL fail. @@ -78,7 +79,7 @@ public function getEntitiesByIds(string $identifier, array $values): array return (string) $v === (string) (int) $v || ctype_digit($v); })); } elseif (\in_array($type, ['ulid', 'uuid', 'guid'])) { - $parameterType = Connection::PARAM_STR_ARRAY; + $parameterType = class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY; // Like above, but we just filter out empty strings. $values = array_values(array_filter($values, function ($v) { @@ -99,7 +100,7 @@ public function getEntitiesByIds(string $identifier, array $values): array unset($value); } } else { - $parameterType = Connection::PARAM_STR_ARRAY; + $parameterType = class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY; } if (!$values) { return []; diff --git a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php index 2a77216552be6..28e268ac7835f 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php @@ -187,7 +187,7 @@ public function testMappingTypeDetection() // The ordinary fixtures contain annotation $mappingType = $method->invoke($this->extension, __DIR__.'/../Fixtures', $container); - $this->assertSame($mappingType, 'annotation'); + $this->assertSame($mappingType, 'attribute'); // In the attribute folder, attributes are used $mappingType = $method->invoke($this->extension, __DIR__.'/../Fixtures/Attribute', $container); @@ -275,9 +275,9 @@ public function testUnrecognizedCacheDriverException() public static function providerBundles() { - yield ['AnnotationsBundle', 'annotation', '/Entity']; - yield ['AnnotationsOneLineBundle', 'annotation', '/Entity']; - yield ['FullEmbeddableAnnotationsBundle', 'annotation', '/Entity']; + yield ['AnnotationsBundle', 'attribute', '/Entity']; + yield ['AnnotationsOneLineBundle', 'attribute', '/Entity']; + yield ['FullEmbeddableAnnotationsBundle', 'attribute', '/Entity']; yield ['AttributesBundle', 'attribute', '/Entity']; yield ['FullEmbeddableAttributesBundle', 'attribute', '/Entity']; yield ['XmlBundle', 'xml', '/Resources/config/doctrine']; @@ -286,7 +286,7 @@ public static function providerBundles() yield ['SrcXmlBundle', 'xml', '/Resources/config/doctrine']; - yield ['NewAnnotationsBundle', 'annotation', \DIRECTORY_SEPARATOR.'src/Entity']; + yield ['NewAnnotationsBundle', 'attribute', \DIRECTORY_SEPARATOR.'src/Entity']; yield ['NewXmlBundle', 'xml', '/config/doctrine']; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/DoctrineTestHelper.php b/src/Symfony/Bridge/Doctrine/Tests/DoctrineTestHelper.php index be18506ba96ae..2cbf416a67b26 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DoctrineTestHelper.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DoctrineTestHelper.php @@ -12,10 +12,15 @@ namespace Symfony\Bridge\Doctrine\Tests; use Doctrine\Common\Annotations\AnnotationReader; +use Doctrine\Common\EventManager; +use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory; use Doctrine\ORM\Configuration; use Doctrine\ORM\EntityManager; use Doctrine\ORM\Mapping\Driver\AnnotationDriver; +use Doctrine\ORM\Mapping\Driver\AttributeDriver; use Doctrine\ORM\Mapping\Driver\XmlDriver; +use Doctrine\ORM\ORMSetup; use Doctrine\Persistence\Mapping\Driver\MappingDriverChain; use Doctrine\Persistence\Mapping\Driver\SymfonyFileLocator; use PHPUnit\Framework\TestCase; @@ -41,17 +46,32 @@ public static function createTestEntityManager(Configuration $config = null): En 'memory' => true, ]; - return EntityManager::create($params, $config ?? self::createTestConfiguration()); + $config ??= self::createTestConfiguration(); + + if (!(new \ReflectionMethod(EntityManager::class, '__construct'))->isPublic()) { + return EntityManager::create($params, $config); + } + + $eventManager = new EventManager(); + + return new EntityManager(DriverManager::getConnection($params, $config, $eventManager), $config, $eventManager); } public static function createTestConfiguration(): Configuration { - $config = new Configuration(); + $config = class_exists(ORMSetup::class) ? ORMSetup::createConfiguration(true) : new Configuration(); $config->setEntityNamespaces(['SymfonyTestsDoctrine' => 'Symfony\Bridge\Doctrine\Tests\Fixtures']); $config->setAutoGenerateProxyClasses(true); $config->setProxyDir(sys_get_temp_dir()); $config->setProxyNamespace('SymfonyTests\Doctrine'); - $config->setMetadataDriverImpl(new AnnotationDriver(new AnnotationReader())); + if (class_exists(AttributeDriver::class)) { + $config->setMetadataDriverImpl(new AttributeDriver([__DIR__.'/../Tests/Fixtures' => 'Symfony\Bridge\Doctrine\Tests\Fixtures'], true)); + } else { + $config->setMetadataDriverImpl(new AnnotationDriver(new AnnotationReader(), null, true)); + } + if (class_exists(DefaultSchemaManagerFactory::class)) { + $config->setSchemaManagerFactory(new DefaultSchemaManagerFactory()); + } return $config; } @@ -65,7 +85,9 @@ public static function createTestConfigurationWithXmlLoader(): Configuration new XmlDriver( new SymfonyFileLocator( [__DIR__.'/../Tests/Resources/orm' => 'Symfony\\Bridge\\Doctrine\\Tests\\Fixtures'], '.orm.xml' - ) + ), + '.orm.xml', + true ), 'Symfony\\Bridge\\Doctrine\\Tests\\Fixtures' ); diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/AssociationEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/AssociationEntity.php index c6d689a96a68c..94be71ec9f153 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/AssociationEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/AssociationEntity.php @@ -16,6 +16,7 @@ /** * @ORM\Entity */ +#[ORM\Entity] class AssociationEntity { /** @@ -23,6 +24,7 @@ class AssociationEntity * @ORM\Id @ORM\GeneratedValue * @ORM\Column(type="integer") */ + #[ORM\Id, ORM\GeneratedValue, ORM\Column(type: 'integer')] private $id; /** @@ -30,6 +32,7 @@ class AssociationEntity * * @var \Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity */ + #[ORM\ManyToOne(targetEntity: SingleIntIdEntity::class)] public $single; /** @@ -41,5 +44,8 @@ class AssociationEntity * * @var \Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIntIdEntity */ + #[ORM\ManyToOne(targetEntity: CompositeIntIdEntity::class)] + #[ORM\JoinColumn(name: 'composite_id1', referencedColumnName: 'id1')] + #[ORM\JoinColumn(name: 'composite_id2', referencedColumnName: 'id2')] public $composite; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/AssociationEntity2.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/AssociationEntity2.php index a0a76124583a0..df4894e76ecbc 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/AssociationEntity2.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/AssociationEntity2.php @@ -16,6 +16,7 @@ /** * @ORM\Entity */ +#[ORM\Entity] class AssociationEntity2 { /** @@ -23,6 +24,7 @@ class AssociationEntity2 * @ORM\Id @ORM\GeneratedValue * @ORM\Column(type="integer") */ + #[ORM\Id, ORM\GeneratedValue, ORM\Column(type: 'integer')] private $id; /** @@ -30,6 +32,7 @@ class AssociationEntity2 * * @var \Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdNoToStringEntity */ + #[ORM\ManyToOne(targetEntity: SingleIntIdNoToStringEntity::class)] public $single; /** @@ -41,5 +44,8 @@ class AssociationEntity2 * * @var \Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIntIdEntity */ + #[ORM\ManyToOne(targetEntity: CompositeIntIdEntity::class)] + #[ORM\JoinColumn(name: 'composite_id1', referencedColumnName: 'id1')] + #[ORM\JoinColumn(name: 'composite_id2', referencedColumnName: 'id2')] public $composite; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsBundle/Entity/Person.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsBundle/Entity/Person.php index 0d7cc91362da3..4f33525506493 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsBundle/Entity/Person.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsBundle/Entity/Person.php @@ -18,12 +18,15 @@ /** * @Entity */ +#[Entity] class Person { /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id; /** @Column(type="string") */ + #[Column(type: 'string')] public $name; public function __construct($id, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsOneLineBundle/Entity/Person.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsOneLineBundle/Entity/Person.php index b55fe6f86503b..0e77dbffc6dff 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsOneLineBundle/Entity/Person.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsOneLineBundle/Entity/Person.php @@ -16,12 +16,15 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class Person { /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id; /** @Column(type="string") */ + #[Column(type: 'string')] public $name; public function __construct($id, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AttributesBundle/AnnotatedEntity/Person.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AttributesBundle/AnnotatedEntity/Person.php index 0ec41bb096861..ae6fec848d1f2 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AttributesBundle/AnnotatedEntity/Person.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AttributesBundle/AnnotatedEntity/Person.php @@ -18,12 +18,15 @@ /** * @Entity */ +#[Entity] class Person { /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id; /** @Column(type="string") */ + #[Column(type: 'string')] public $name; public function __construct($id, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAnnotationsBundle/Entity/Address.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAnnotationsBundle/Entity/Address.php index d311a3f1ad1a1..bad7402e51c95 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAnnotationsBundle/Entity/Address.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAnnotationsBundle/Entity/Address.php @@ -13,21 +13,24 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Embeddable; -use Doctrine\ORM\Mapping\Id; /** * @Embeddable */ +#[Embeddable] class Address { /** @Column(type="string") */ + #[Column(type: 'string')] public $street; /** @Column(type="string") */ + #[Column(type: 'string')] public $zipCode; /** @Column(type="string") */ + #[Column(type: 'string')] public $city; public function __construct($street, $zipCode, $city) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAttributesBundle/Entity/Address.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAttributesBundle/Entity/Address.php index c0c58d6a21ce2..e257442ae18e6 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAttributesBundle/Entity/Address.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAttributesBundle/Entity/Address.php @@ -13,7 +13,6 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Embeddable; -use Doctrine\ORM\Mapping\Id; #[Embeddable] class Address diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/NewAnnotationsBundle/src/Entity/Person.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/NewAnnotationsBundle/src/Entity/Person.php index e94a24e1a95c7..9ab508e30523f 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/NewAnnotationsBundle/src/Entity/Person.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/NewAnnotationsBundle/src/Entity/Person.php @@ -18,12 +18,15 @@ /** * @Entity */ +#[Entity] class Person { /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id; /** @Column(type="string") */ + #[Column(type: 'string')] public $name; public function __construct($id, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeIntIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeIntIdEntity.php index 7c64cc20ad7e1..40ff64d9488b3 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeIntIdEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeIntIdEntity.php @@ -16,15 +16,19 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class CompositeIntIdEntity { /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id1; /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id2; /** @Column(type="string") */ + #[Column(type: 'string')] public $name; public function __construct($id1, $id2, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeObjectNoToStringIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeObjectNoToStringIdEntity.php index 73a6419e2f0c0..67f3c4f96c07a 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeObjectNoToStringIdEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeObjectNoToStringIdEntity.php @@ -18,6 +18,7 @@ * * @ORM\Entity */ +#[ORM\Entity] class CompositeObjectNoToStringIdEntity { /** @@ -27,6 +28,9 @@ class CompositeObjectNoToStringIdEntity * @ORM\ManyToOne(targetEntity="SingleIntIdNoToStringEntity", cascade={"persist"}) * @ORM\JoinColumn(name="object_one_id") */ + #[ORM\Id] + #[ORM\ManyToOne(targetEntity: SingleIntIdNoToStringEntity::class, cascade: ['persist'])] + #[ORM\JoinColumn(name: 'object_one_id')] protected $objectOne; /** @@ -36,6 +40,9 @@ class CompositeObjectNoToStringIdEntity * @ORM\ManyToOne(targetEntity="SingleIntIdNoToStringEntity", cascade={"persist"}) * @ORM\JoinColumn(name="object_two_id") */ + #[ORM\Id] + #[ORM\ManyToOne(targetEntity: SingleIntIdNoToStringEntity::class, cascade: ['persist'])] + #[ORM\JoinColumn(name: 'object_two_id')] protected $objectTwo; public function __construct(SingleIntIdNoToStringEntity $objectOne, SingleIntIdNoToStringEntity $objectTwo) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeStringIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeStringIdEntity.php index d6e8d2cd2aafa..95e687f6bfa50 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeStringIdEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeStringIdEntity.php @@ -16,15 +16,19 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class CompositeStringIdEntity { /** @Id @Column(type="string") */ + #[Id, Column(type: 'string')] protected $id1; /** @Id @Column(type="string") */ + #[Id, Column(type: 'string')] protected $id2; /** @Column(type="string") */ + #[Column(type: 'string')] public $name; public function __construct($id1, $id2, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEmbed.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEmbed.php index fc16f1cc135bc..5912ae6b1f9ea 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEmbed.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEmbed.php @@ -16,15 +16,18 @@ /** * @ORM\Embeddable */ +#[ORM\Embeddable] class DoctrineLoaderEmbed { /** * @ORM\Column(length=25) */ + #[ORM\Column(length: 25)] public $embeddedMaxLength; /** * @ORM\Embedded(class=DoctrineLoaderNestedEmbed::class) */ + #[ORM\Embedded(class: DoctrineLoaderNestedEmbed::class)] public $nestedEmbedded; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEntity.php index d6aee2d18b0b1..c32a9ef49d472 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEntity.php @@ -21,58 +21,70 @@ * * @author Kévin Dunglas */ +#[ORM\Entity, UniqueEntity(fields: ["alreadyMappedUnique"])] class DoctrineLoaderEntity extends DoctrineLoaderParentEntity { /** * @ORM\Id * @ORM\Column */ + #[ORM\Id, ORM\Column] public $id; /** * @ORM\Column(length=20) */ + #[ORM\Column(length: 20)] public $maxLength; /** * @ORM\Column(length=20) * @Assert\Length(min=5) */ + #[ORM\Column(length: 20), Assert\Length(min: 5)] public $mergedMaxLength; /** * @ORM\Column(length=20) * @Assert\Length(min=1, max=10) */ + #[ORM\Column(length: 20), Assert\Length(min: 1, max: 10)] public $alreadyMappedMaxLength; /** * @ORM\Column(unique=true) */ + #[ORM\Column(unique: true)] public $unique; /** * @ORM\Column(unique=true) */ + #[ORM\Column(unique: true)] public $alreadyMappedUnique; /** * @ORM\Embedded(class=DoctrineLoaderEmbed::class) */ + #[ORM\Embedded(class: DoctrineLoaderEmbed::class)] public $embedded; /** @ORM\Column(type="text", nullable=true, length=1000) */ + #[ORM\Column(type: 'text', nullable: true, length: 1000)] public $textField; /** @ORM\Id @ORM\Column(type="guid", length=50) */ + #[ORM\Id, ORM\Column(type: 'guid', length: 50)] protected $guidField; /** @ORM\Column(type="simple_array", length=100) */ + #[ORM\Column(type: 'simple_array', length: 100)] public $simpleArrayField = []; /** * @ORM\Column(length=10) * @Assert\DisableAutoMapping */ + #[ORM\Column(length: 10), Assert\DisableAutoMapping] public $noAutoMapping; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEnum.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEnum.php index 8ac883e89c4a2..4ba7211456902 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEnum.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEnum.php @@ -12,25 +12,31 @@ namespace Symfony\Bridge\Doctrine\Tests\Fixtures; use Doctrine\ORM\Mapping as ORM; +use Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\EnumInt; +use Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\EnumString; /** * @ORM\Entity */ +#[ORM\Entity] class DoctrineLoaderEnum { /** * @ORM\Id * @ORM\Column */ + #[ORM\Id, ORM\Column] public $id; /** * @ORM\Column(type="string", enumType="Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\EnumString", length=1) */ + #[ORM\Column(type: 'string', enumType: EnumString::class, length: 1)] public $enumString; /** * @ORM\Column(type="integer", enumType="Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\EnumInt") */ + #[ORM\Column(type: 'integer', enumType: EnumInt::class)] public $enumInt; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderNestedEmbed.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderNestedEmbed.php index fbf41555a1316..9d9424f0ece0d 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderNestedEmbed.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderNestedEmbed.php @@ -16,10 +16,12 @@ /** * @ORM\Embeddable() */ +#[ORM\Embeddable] class DoctrineLoaderNestedEmbed { /** * @ORM\Column(length=27) */ + #[ORM\Column(length: 27)] public $nestedEmbeddedMaxLength; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderNoAutoMappingEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderNoAutoMappingEntity.php index 0914411431201..515ec30bc897f 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderNoAutoMappingEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderNoAutoMappingEntity.php @@ -20,22 +20,26 @@ * * @author Kévin Dunglas */ +#[ORM\Entity, Assert\DisableAutoMapping] class DoctrineLoaderNoAutoMappingEntity { /** * @ORM\Id * @ORM\Column */ + #[ORM\Id, ORM\Column] public $id; /** * @ORM\Column(length=20, unique=true) */ + #[ORM\Column(length: 20, unique: true)] public $maxLength; /** * @Assert\EnableAutoMapping * @ORM\Column(length=20) */ + #[Assert\EnableAutoMapping, ORM\Column(length: 20)] public $autoMappingExplicitlyEnabled; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderParentEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderParentEntity.php index 7ec0263559c71..d7d832e6af23a 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderParentEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderParentEntity.php @@ -16,16 +16,19 @@ /** * @ORM\MappedSuperclass */ +#[ORM\MappedSuperclass] class DoctrineLoaderParentEntity { /** * @ORM\Column(length=35) */ + #[ORM\Column(length: 35)] public $publicParentMaxLength; /** * @ORM\Column(length=30) */ + #[ORM\Column(length: 30)] private $privateParentMaxLength; public function getPrivateParentMaxLength() diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoubleNameEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoubleNameEntity.php index 3559568787bcd..2c8ac68ce17c2 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoubleNameEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoubleNameEntity.php @@ -16,15 +16,19 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class DoubleNameEntity { /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id; /** @Column(type="string") */ + #[Column(type: 'string')] public $name; /** @Column(type="string", nullable=true) */ + #[Column(type: 'string', nullable: true)] public $name2; public function __construct($id, $name, $name2) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoubleNullableNameEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoubleNullableNameEntity.php index 20ef14fd1b578..0c37651412ab4 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoubleNullableNameEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoubleNullableNameEntity.php @@ -16,15 +16,19 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class DoubleNullableNameEntity { /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id; /** @Column(type="string", nullable=true) */ + #[Column(type: 'string', nullable: true)] public $name; /** @Column(type="string", nullable=true) */ + #[Column(type: 'string', nullable: true)] public $name2; public function __construct($id, $name, $name2) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Embeddable/Identifier.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Embeddable/Identifier.php index f8000dbfd9814..cdc82cf94e34a 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Embeddable/Identifier.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Embeddable/Identifier.php @@ -16,6 +16,7 @@ /** * @ORM\Embeddable */ +#[ORM\Embeddable] class Identifier { /** @@ -24,5 +25,6 @@ class Identifier * @ORM\Id * @ORM\Column(type="integer") */ + #[ORM\Id, ORM\Column(type: 'integer')] protected $value; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/EmbeddedIdentifierEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/EmbeddedIdentifierEntity.php index 6d7b2670962c7..c2cec427ad77e 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/EmbeddedIdentifierEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/EmbeddedIdentifierEntity.php @@ -12,10 +12,12 @@ namespace Symfony\Bridge\Doctrine\Tests\Fixtures; use Doctrine\ORM\Mapping as ORM; +use Symfony\Bridge\Doctrine\Tests\Fixtures\Embeddable\Identifier; /** * @ORM\Entity */ +#[ORM\Entity] class EmbeddedIdentifierEntity { /** @@ -23,5 +25,6 @@ class EmbeddedIdentifierEntity * * @ORM\Embedded(class="Symfony\Bridge\Doctrine\Tests\Fixtures\Embeddable\Identifier") */ + #[ORM\Embedded(class: Identifier::class)] protected $id; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Employee.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Employee.php index 24f08b00d781a..b9888adc8beae 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Employee.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Employee.php @@ -14,6 +14,7 @@ use Doctrine\ORM\Mapping\Entity; /** @Entity */ +#[Entity] class Employee extends Person { } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/GroupableEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/GroupableEntity.php index 730a9b2b1dbf8..d803ca2310d8b 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/GroupableEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/GroupableEntity.php @@ -16,15 +16,19 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class GroupableEntity { /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id; /** @Column(type="string", nullable=true) */ + #[Column(type: 'string', nullable: true)] public $name; /** @Column(type="string", nullable=true) */ + #[Column(type: 'string', nullable: true)] public $groupName; public function __construct($id, $name, $groupName) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/GuidIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/GuidIdEntity.php index 0d447ffc1e62c..d575659595010 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/GuidIdEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/GuidIdEntity.php @@ -16,9 +16,11 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class GuidIdEntity { /** @Id @Column(type="guid") */ + #[Id, Column(type: 'guid')] protected $id; public function __construct($id) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Person.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Person.php index b90a54ac02c61..35f3836e0d901 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Person.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Person.php @@ -24,12 +24,15 @@ * @DiscriminatorColumn(name="discr", type="string") * @DiscriminatorMap({"person" = "Person", "employee" = "Employee"}) */ +#[Entity, InheritanceType('SINGLE_TABLE'), DiscriminatorColumn(name: 'discr', type: 'string'), DiscriminatorMap(['person' => 'Person', 'employee' => 'Employee'])] class Person { /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id; /** @Column(type="string") */ + #[Column(type: 'string')] public $name; public function __construct($id, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleAssociationToIntIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleAssociationToIntIdEntity.php index bed8bb9a51d01..b4a1ede9f0a2a 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleAssociationToIntIdEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleAssociationToIntIdEntity.php @@ -17,12 +17,15 @@ use Doctrine\ORM\Mapping\OneToOne; /** @Entity */ +#[Entity] class SingleAssociationToIntIdEntity { /** @Id @OneToOne(targetEntity="SingleIntIdNoToStringEntity", cascade={"ALL"}) */ + #[Id, OneToOne(targetEntity: SingleIntIdNoToStringEntity::class, cascade: ['ALL'])] protected $entity; /** @Column(type="string", nullable=true) */ + #[Column(type: 'string', nullable: true)] public $name; public function __construct(SingleIntIdNoToStringEntity $entity, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdEntity.php index 612566b45d94b..94b47da855a37 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdEntity.php @@ -16,15 +16,19 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class SingleIntIdEntity { /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id; /** @Column(type="string", nullable=true) */ + #[Column(type: 'string', nullable: true)] public $name; /** @Column(type="array", nullable=true) */ + #[Column(type: 'array', nullable: true)] public $phoneNumbers = []; public function __construct($id, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdNoToStringEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdNoToStringEntity.php index c94815ca02cad..8887c5b5a3d6c 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdNoToStringEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdNoToStringEntity.php @@ -16,12 +16,15 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class SingleIntIdNoToStringEntity { /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id; /** @Column(type="string", nullable=true) */ + #[Column(type: 'string', nullable: true)] public $name; public function __construct($id, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdStringWrapperNameEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdStringWrapperNameEntity.php index 8f4c3663d2148..8c774009ba530 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdStringWrapperNameEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdStringWrapperNameEntity.php @@ -16,12 +16,15 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class SingleIntIdStringWrapperNameEntity { /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id; /** @Column(type="string_wrapper", nullable=true) */ + #[Column(type: 'string_wrapper', nullable: true)] public $name; public function __construct($id, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleStringCastableIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleStringCastableIdEntity.php index 128801a02c922..b3e952d9a8b33 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleStringCastableIdEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleStringCastableIdEntity.php @@ -17,6 +17,7 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class SingleStringCastableIdEntity { /** @@ -24,9 +25,11 @@ class SingleStringCastableIdEntity * @Column(type="string") * @GeneratedValue(strategy="NONE") */ + #[Id, Column(type: 'string'), GeneratedValue(strategy: 'NONE')] protected $id; /** @Column(type="string", nullable=true) */ + #[Column(type: 'string', nullable: true)] public $name; public function __construct($id, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleStringIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleStringIdEntity.php index 83f7a9f9ab39d..ce3a5526a5b15 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleStringIdEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleStringIdEntity.php @@ -16,12 +16,15 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class SingleStringIdEntity { /** @Id @Column(type="string") */ + #[Id, Column(type: 'string')] protected $id; /** @Column(type="string") */ + #[Column(type: 'string')] public $name; public function __construct($id, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/UlidIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/UlidIdEntity.php index 3ee909fe4bfc5..238a08511ebe9 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/UlidIdEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/UlidIdEntity.php @@ -16,9 +16,11 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class UlidIdEntity { /** @Id @Column(type="ulid") */ + #[Id, Column(type: 'ulid')] protected $id; public function __construct($id) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/User.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/User.php index b8b10094dd6bd..8d63b19b0313f 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/User.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/User.php @@ -18,15 +18,19 @@ use Symfony\Component\Security\Core\User\UserInterface; /** @Entity */ +#[Entity] class User implements UserInterface, PasswordAuthenticatedUserInterface { /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id1; /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id2; /** @Column(type="string") */ + #[Column(type: 'string')] public $name; public function __construct($id1, $id2, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/UuidIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/UuidIdEntity.php index 46084ab292d49..60271f7d15ab0 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/UuidIdEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/UuidIdEntity.php @@ -16,9 +16,11 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class UuidIdEntity { /** @Id @Column(type="uuid") */ + #[Id, Column(type: 'uuid')] protected $id; public function __construct($id) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php index 3a626bb1e02d4..f52a1ce1e93c4 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php @@ -11,6 +11,7 @@ namespace Symfony\Bridge\Doctrine\Tests\Form\ChoiceList; +use Doctrine\DBAL\ArrayParameterType; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Result; use Doctrine\DBAL\Types\GuidType; @@ -35,12 +36,12 @@ protected function tearDown(): void public function testIdentifierTypeIsStringArray() { - $this->checkIdentifierType('Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringIdEntity', Connection::PARAM_STR_ARRAY); + $this->checkIdentifierType('Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringIdEntity', class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY); } public function testIdentifierTypeIsIntegerArray() { - $this->checkIdentifierType('Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity', Connection::PARAM_INT_ARRAY); + $this->checkIdentifierType('Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity', class_exists(ArrayParameterType::class) ? ArrayParameterType::INTEGER : Connection::PARAM_INT_ARRAY); } protected function checkIdentifierType($classname, $expectedType) @@ -90,7 +91,7 @@ public function testFilterNonIntegerValues() $query->expects($this->once()) ->method('setParameter') - ->with('ORMQueryBuilderLoader_getEntitiesByIds_id', [1, 2, 3, '9223372036854775808'], Connection::PARAM_INT_ARRAY) + ->with('ORMQueryBuilderLoader_getEntitiesByIds_id', [1, 2, 3, '9223372036854775808'], class_exists(ArrayParameterType::class) ? ArrayParameterType::INTEGER : Connection::PARAM_INT_ARRAY) ->willReturn($query); $qb = $this->getMockBuilder(\Doctrine\ORM\QueryBuilder::class) @@ -126,7 +127,7 @@ public function testFilterEmptyUuids($entityClass) $query->expects($this->once()) ->method('setParameter') - ->with('ORMQueryBuilderLoader_getEntitiesByIds_id', ['71c5fd46-3f16-4abb-bad7-90ac1e654a2d', 'b98e8e11-2897-44df-ad24-d2627eb7f499'], Connection::PARAM_STR_ARRAY) + ->with('ORMQueryBuilderLoader_getEntitiesByIds_id', ['71c5fd46-3f16-4abb-bad7-90ac1e654a2d', 'b98e8e11-2897-44df-ad24-d2627eb7f499'], class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY) ->willReturn($query); $qb = $this->getMockBuilder(\Doctrine\ORM\QueryBuilder::class) @@ -171,7 +172,7 @@ public function testFilterUid($entityClass) $query->expects($this->once()) ->method('setParameter') - ->with('ORMQueryBuilderLoader_getEntitiesByIds_id', [Uuid::fromString('71c5fd46-3f16-4abb-bad7-90ac1e654a2d')->toBinary(), Uuid::fromString('b98e8e11-2897-44df-ad24-d2627eb7f499')->toBinary()], Connection::PARAM_STR_ARRAY) + ->with('ORMQueryBuilderLoader_getEntitiesByIds_id', [Uuid::fromString('71c5fd46-3f16-4abb-bad7-90ac1e654a2d')->toBinary(), Uuid::fromString('b98e8e11-2897-44df-ad24-d2627eb7f499')->toBinary()], class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY) ->willReturn($query); $qb = $this->getMockBuilder(\Doctrine\ORM\QueryBuilder::class) @@ -239,7 +240,7 @@ public function testEmbeddedIdentifierName() $query->expects($this->once()) ->method('setParameter') - ->with('ORMQueryBuilderLoader_getEntitiesByIds_id_value', [1, 2, 3], Connection::PARAM_INT_ARRAY) + ->with('ORMQueryBuilderLoader_getEntitiesByIds_id_value', [1, 2, 3], class_exists(ArrayParameterType::class) ? ArrayParameterType::INTEGER : Connection::PARAM_INT_ARRAY) ->willReturn($query); $qb = $this->getMockBuilder(\Doctrine\ORM\QueryBuilder::class) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Middleware/Debug/MiddlewareTest.php b/src/Symfony/Bridge/Doctrine/Tests/Middleware/Debug/MiddlewareTest.php index 2bb628442459c..b1096fbf60cb5 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Middleware/Debug/MiddlewareTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Middleware/Debug/MiddlewareTest.php @@ -17,8 +17,10 @@ use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Result; +use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory; use Doctrine\DBAL\Statement; use Doctrine\DBAL\Types\Types; +use Doctrine\ORM\ORMSetup; use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\Middleware\Debug\DebugDataHolder; use Symfony\Bridge\Doctrine\Middleware\Debug\Middleware; @@ -49,14 +51,17 @@ private function init(bool $withStopwatch = true): void { $this->stopwatch = $withStopwatch ? new Stopwatch() : null; - $configuration = new Configuration(); + $config = class_exists(ORMSetup::class) ? ORMSetup::createConfiguration(true) : new Configuration(); + if (class_exists(DefaultSchemaManagerFactory::class)) { + $config->setSchemaManagerFactory(new DefaultSchemaManagerFactory()); + } $this->debugDataHolder = new DebugDataHolder(); - $configuration->setMiddlewares([new Middleware($this->debugDataHolder, $this->stopwatch)]); + $config->setMiddlewares([new Middleware($this->debugDataHolder, $this->stopwatch)]); $this->conn = DriverManager::getConnection([ 'driver' => 'pdo_sqlite', 'memory' => true, - ], $configuration); + ], $config); $this->conn->executeQuery(<<getResourceFromString('mydata'); $stmt = $this->conn->prepare($sql); - $stmt->bindParam(1, $product); - $stmt->bindParam(2, $price); - $stmt->bindParam(3, $stock, ParameterType::INTEGER); - $stmt->bindParam(4, $res, ParameterType::BINARY); - - $product = 'product1'; - $price = 12.5; - $stock = 5; + $stmt->bindValue(1, 'product1'); + $stmt->bindValue(2, '12.5'); + $stmt->bindValue(3, 5, ParameterType::INTEGER); + $stmt->bindValue(4, $res, ParameterType::BINARY); $executeMethod($stmt); @@ -168,7 +169,7 @@ public function testWithParamBound(callable $executeMethod) $debug = $this->debugDataHolder->getData()['default'] ?? []; $this->assertCount(2, $debug); $this->assertSame($sql, $debug[1]['sql']); - $this->assertSame(['product1', 12.5, 5, $expectedRes], $debug[1]['params']); + $this->assertSame(['product1', '12.5', 5, $expectedRes], $debug[1]['params']); $this->assertSame([ParameterType::STRING, ParameterType::STRING, ParameterType::INTEGER, ParameterType::BINARY], $debug[1]['types']); $this->assertGreaterThan(0, $debug[1]['executionMS']); } @@ -188,6 +189,7 @@ public function testTransaction(callable $endTransactionMethod, string $expected { $this->init(); + $this->conn->setNestTransactionsWithSavepoints(true); $this->conn->beginTransaction(); $this->conn->beginTransaction(); $this->conn->executeStatement('INSERT INTO products(name, price, stock) VALUES ("product1", 12.5, 5)'); @@ -198,19 +200,23 @@ public function testTransaction(callable $endTransactionMethod, string $expected $endTransactionMethod($this->conn); $debug = $this->debugDataHolder->getData()['default'] ?? []; - $this->assertCount(7, $debug); + $this->assertCount(9, $debug); $this->assertSame('"START TRANSACTION"', $debug[1]['sql']); $this->assertGreaterThan(0, $debug[1]['executionMS']); - $this->assertSame('INSERT INTO products(name, price, stock) VALUES ("product1", 12.5, 5)', $debug[2]['sql']); + $this->assertSame('SAVEPOINT DOCTRINE2_SAVEPOINT_2', $debug[2]['sql']); $this->assertGreaterThan(0, $debug[2]['executionMS']); - $this->assertSame($expectedEndTransactionDebug, $debug[3]['sql']); + $this->assertSame('INSERT INTO products(name, price, stock) VALUES ("product1", 12.5, 5)', $debug[3]['sql']); $this->assertGreaterThan(0, $debug[3]['executionMS']); - $this->assertSame('"START TRANSACTION"', $debug[4]['sql']); + $this->assertSame(('"ROLLBACK"' === $expectedEndTransactionDebug ? 'ROLLBACK TO' : 'RELEASE').' SAVEPOINT DOCTRINE2_SAVEPOINT_2', $debug[4]['sql']); $this->assertGreaterThan(0, $debug[4]['executionMS']); - $this->assertSame('INSERT INTO products(name, price, stock) VALUES ("product2", 15.5, 12)', $debug[5]['sql']); + $this->assertSame($expectedEndTransactionDebug, $debug[5]['sql']); $this->assertGreaterThan(0, $debug[5]['executionMS']); - $this->assertSame($expectedEndTransactionDebug, $debug[6]['sql']); + $this->assertSame('"START TRANSACTION"', $debug[6]['sql']); $this->assertGreaterThan(0, $debug[6]['executionMS']); + $this->assertSame('INSERT INTO products(name, price, stock) VALUES ("product2", 15.5, 12)', $debug[7]['sql']); + $this->assertGreaterThan(0, $debug[7]['executionMS']); + $this->assertSame($expectedEndTransactionDebug, $debug[8]['sql']); + $this->assertGreaterThan(0, $debug[8]['executionMS']); } public static function provideExecuteAndEndTransactionMethods(): array diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php index 631d8035bfaa1..225b4217ae242 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php @@ -11,10 +11,16 @@ namespace Symfony\Bridge\Doctrine\Tests\PropertyInfo; +use Doctrine\Common\Annotations\AnnotationReader; use Doctrine\Common\Collections\Collection; +use Doctrine\Common\EventManager; +use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory; use Doctrine\DBAL\Types\Type as DBALType; use Doctrine\ORM\EntityManager; use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Driver\AnnotationDriver; +use Doctrine\ORM\Mapping\Driver\AttributeDriver; use Doctrine\ORM\ORMSetup; use Doctrine\ORM\Tools\Setup; use PHPUnit\Framework\TestCase; @@ -35,9 +41,23 @@ class DoctrineExtractorTest extends TestCase private function createExtractor() { $config = class_exists(ORMSetup::class) - ? ORMSetup::createAnnotationMetadataConfiguration([__DIR__.\DIRECTORY_SEPARATOR.'Fixtures'], true) + ? ORMSetup::createConfiguration(true) : Setup::createAnnotationMetadataConfiguration([__DIR__.\DIRECTORY_SEPARATOR.'Fixtures'], true); - $entityManager = EntityManager::create(['driver' => 'pdo_sqlite'], $config); + if (\PHP_VERSION_ID >= 80000 && class_exists(AttributeDriver::class)) { + $config->setMetadataDriverImpl(new AttributeDriver([__DIR__.'/../Tests/Fixtures' => 'Symfony\Bridge\Doctrine\Tests\Fixtures'], true)); + } else { + $config->setMetadataDriverImpl(new AnnotationDriver(new AnnotationReader(), null, true)); + } + if (class_exists(DefaultSchemaManagerFactory::class)) { + $config->setSchemaManagerFactory(new DefaultSchemaManagerFactory()); + } + + if (!(new \ReflectionMethod(EntityManager::class, '__construct'))->isPublic()) { + $entityManager = EntityManager::create(['driver' => 'pdo_sqlite'], $config); + } else { + $eventManager = new EventManager(); + $entityManager = new EntityManager(DriverManager::getConnection(['driver' => 'pdo_sqlite'], $config, $eventManager), $config, $eventManager); + } if (!DBALType::hasType('foo')) { DBALType::addType('foo', 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineFooType'); diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php index 8190540e27566..902850c5828ba 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php @@ -23,102 +23,122 @@ * * @author Kévin Dunglas */ +#[Entity] class DoctrineDummy { /** * @Id * @Column(type="smallint") */ + #[Id, Column(type: 'smallint')] public $id; /** * @ManyToOne(targetEntity="DoctrineRelation") */ + #[ManyToOne(targetEntity: DoctrineRelation::class)] public $foo; /** * @ManyToMany(targetEntity="DoctrineRelation") */ + #[ManyToMany(targetEntity: DoctrineRelation::class)] public $bar; /** * @ManyToMany(targetEntity="DoctrineRelation", indexBy="rguid") */ + #[ManyToMany(targetEntity: DoctrineRelation::class, indexBy: 'rguid')] protected $indexedRguid; /** * @ManyToMany(targetEntity="DoctrineRelation", indexBy="rguid_column") */ + #[ManyToMany(targetEntity: DoctrineRelation::class, indexBy: 'rguid_column')] protected $indexedBar; /** * @OneToMany(targetEntity="DoctrineRelation", mappedBy="foo", indexBy="foo") */ + #[OneToMany(targetEntity: DoctrineRelation::class, mappedBy: 'foo', indexBy: 'foo')] protected $indexedFoo; /** * @OneToMany(targetEntity="DoctrineRelation", mappedBy="baz", indexBy="baz_id") */ + #[OneToMany(targetEntity: DoctrineRelation::class, mappedBy: 'baz', indexBy: 'baz_id')] protected $indexedBaz; /** * @Column(type="guid") */ + #[Column(type: 'guid')] protected $guid; /** * @Column(type="time") */ + #[Column(type: 'time')] private $time; /** * @Column(type="time_immutable") */ + #[Column(type: 'time_immutable')] private $timeImmutable; /** * @Column(type="dateinterval") */ + #[Column(type: 'dateinterval')] private $dateInterval; /** * @Column(type="json_array") */ + #[Column(type: 'json_array')] private $jsonArray; /** * @Column(type="simple_array") */ + #[Column(type: 'simple_array')] private $simpleArray; /** * @Column(type="float") */ + #[Column(type: 'float')] private $float; /** * @Column(type="decimal", precision=10, scale=2) */ + #[Column(type: 'decimal', precision: 10, scale: 2)] private $decimal; /** * @Column(type="boolean") */ + #[Column(type: 'boolean')] private $bool; /** * @Column(type="binary") */ + #[Column(type: 'binary')] private $binary; /** * @Column(type="custom_foo") */ + #[Column(type: 'custom_foo')] private $customFoo; /** * @Column(type="bigint") */ + #[Column(type: 'bigint')] private $bigint; public $notMapped; @@ -126,25 +146,30 @@ class DoctrineDummy /** * @OneToMany(targetEntity="DoctrineRelation", mappedBy="dt", indexBy="dt") */ + #[OneToMany(targetEntity: DoctrineRelation::class, mappedBy: 'dt', indexBy: 'dt')] protected $indexedByDt; /** * @OneToMany(targetEntity="DoctrineRelation", mappedBy="customType", indexBy="customType") */ + #[OneToMany(targetEntity: DoctrineRelation::class, mappedBy: 'customType', indexBy: 'customType')] private $indexedByCustomType; /** * @OneToMany(targetEntity="DoctrineRelation", mappedBy="buzField", indexBy="buzField") */ + #[OneToMany(targetEntity: DoctrineRelation::class, mappedBy: 'buzField', indexBy: 'buzField')] protected $indexedBuz; /** * @Column(type="json", nullable=true) */ + #[Column(type: 'json', nullable: true)] private $json; /** * @OneToMany(targetEntity="DoctrineRelation", mappedBy="dummyRelation", indexBy="gen_value_col_id", orphanRemoval=true) */ + #[OneToMany(targetEntity: DoctrineRelation::class, mappedBy: 'dummyRelation', indexBy: 'gen_value_col_id', orphanRemoval: true)] protected $dummyGeneratedValueList; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineEmbeddable.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineEmbeddable.php index a00856ed7331e..23609fb1827d0 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineEmbeddable.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineEmbeddable.php @@ -19,10 +19,12 @@ * * @author Udaltsov Valentin */ +#[Embeddable] class DoctrineEmbeddable { /** * @Column(type="string") */ + #[Column(type: 'string')] protected $field; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineEnum.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineEnum.php index 8a63a4e8db248..edd6b01e9f5c6 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineEnum.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineEnum.php @@ -18,36 +18,43 @@ /** * @Entity */ +#[Entity] class DoctrineEnum { /** * @Id * @Column(type="smallint") */ + #[Id, Column(type: 'smallint')] public $id; /** * @Column(type="string", enumType="Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\EnumString") */ + #[Column(type: 'string', enumType: EnumString::class)] protected $enumString; /** * @Column(type="integer", enumType="Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\EnumInt") */ + #[Column(type: 'integer', enumType: EnumInt::class)] protected $enumInt; /** * @Column(type="array", enumType="Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\EnumString") */ + #[Column(type: 'array', enumType: EnumString::class)] protected $enumStringArray; /** * @Column(type="simple_array", enumType="Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\EnumInt") */ + #[Column(type: 'simple_array', enumType: EnumInt::class)] protected $enumIntArray; /** * @Column(type="custom_foo", enumType="Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\EnumInt") */ + #[Column(type: 'custom_foo', enumType: EnumInt::class)] protected $enumCustom; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineGeneratedValue.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineGeneratedValue.php index 9e7612fa35ae4..90fec268506e8 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineGeneratedValue.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineGeneratedValue.php @@ -22,6 +22,7 @@ * * @Entity */ +#[Entity] class DoctrineGeneratedValue { /** @@ -29,21 +30,25 @@ class DoctrineGeneratedValue * @GeneratedValue(strategy="AUTO") * @Column(type="integer") */ + #[Id, GeneratedValue(strategy: 'AUTO'), Column(type: 'integer')] public $id; /** * @Column */ + #[Column] public $foo; /** * @var int * @Column(type="integer", name="gen_value_col_id") */ + #[Column(type: 'integer', name: 'gen_value_col_id')] public $valueId; /** * @OneToMany(targetEntity="DoctrineRelation", mappedBy="generatedValueRelation", indexBy="rguid_column", orphanRemoval=true) */ + #[OneToMany(targetEntity: DoctrineRelation::class, mappedBy: 'generatedValueRelation', indexBy: 'rguid_column', orphanRemoval: true)] protected $relationList; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php index 91115abb719ec..3310f9e0d06a0 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php @@ -22,54 +22,67 @@ * * @author Kévin Dunglas */ +#[Entity] class DoctrineRelation { /** * @Id * @Column(type="smallint") */ + #[Id, Column(type: 'smallint')] public $id; /** * @Column(type="guid", name="rguid_column") */ + #[Column(type: 'guid', name: 'rguid_column')] protected $rguid; /** * @Column(type="guid") * @ManyToOne(targetEntity="DoctrineDummy", inversedBy="indexedFoo") */ + #[Column(type: 'guid')] + #[ManyToOne(targetEntity: DoctrineDummy::class, inversedBy: 'indexedFoo')] protected $foo; /** * @ManyToOne(targetEntity="DoctrineDummy") */ + #[ManyToOne(targetEntity: DoctrineDummy::class)] protected $baz; /** * @Column(type="datetime") */ + #[Column(type: 'datetime')] private $dt; /** * @Column(type="foo") */ + #[Column(type: 'foo')] private $customType; /** * @Column(type="guid", name="different_than_field") * @ManyToOne(targetEntity="DoctrineDummy", inversedBy="indexedBuz") */ + #[Column(type: 'guid', name: 'different_than_field')] + #[ManyToOne(targetEntity: DoctrineDummy::class, inversedBy: 'indexedBuz')] protected $buzField; /** * @ManyToOne(targetEntity="DoctrineDummy", inversedBy="dummyGeneratedValueList") */ + #[ManyToOne(targetEntity: DoctrineDummy::class, inversedBy: 'dummyGeneratedValueList')] private $dummyRelation; /** * @ManyToOne(targetEntity="DoctrineGeneratedValue", inversedBy="relationList") * @JoinColumn(name="gen_value_col_id", referencedColumnName="gen_value_col_id") */ + #[ManyToOne(targetEntity: DoctrineGeneratedValue::class, inversedBy: 'relationList')] + #[JoinColumn(name: 'gen_value_col_id', referencedColumnName: 'gen_value_col_id')] private $generatedValueRelation; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineWithEmbedded.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineWithEmbedded.php index aace866128b0e..053f8bec0d19b 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineWithEmbedded.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineWithEmbedded.php @@ -21,16 +21,19 @@ * * @author Udaltsov Valentin */ +#[Entity] class DoctrineWithEmbedded { /** * @Id * @Column(type="smallint") */ + #[Id, Column(type: 'smallint')] public $id; /** * @Embedded(class="DoctrineEmbeddable") */ + #[Embedded(class: DoctrineEmbeddable::class)] protected $embedded; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Security/RememberMe/DoctrineTokenProviderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Security/RememberMe/DoctrineTokenProviderTest.php index 9d8b9256f4c9d..eb387e424cd09 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Security/RememberMe/DoctrineTokenProviderTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Security/RememberMe/DoctrineTokenProviderTest.php @@ -11,7 +11,10 @@ namespace Security\RememberMe; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory; +use Doctrine\ORM\ORMSetup; use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\Security\RememberMe\DoctrineTokenProvider; use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken; @@ -120,10 +123,15 @@ public function testVerifyOutdatedTokenAfterParallelRequestFailsAfter60Seconds() */ private function bootstrapProvider() { + $config = class_exists(ORMSetup::class) ? ORMSetup::createConfiguration(true) : new Configuration(); + if (class_exists(DefaultSchemaManagerFactory::class)) { + $config->setSchemaManagerFactory(new DefaultSchemaManagerFactory()); + } + $connection = DriverManager::getConnection([ 'driver' => 'pdo_sqlite', - 'url' => 'sqlite:///:memory:', - ]); + 'memory' => true, + ], $config); $connection->{method_exists($connection, 'executeStatement') ? 'executeStatement' : 'executeUpdate'}(<<< 'SQL' CREATE TABLE rememberme_token ( series char(88) UNIQUE PRIMARY KEY NOT NULL, diff --git a/src/Symfony/Bridge/Doctrine/Tests/Types/UlidTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Types/UlidTypeTest.php index 06114aeea0050..c1db2bbe70124 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Types/UlidTypeTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Types/UlidTypeTest.php @@ -12,6 +12,7 @@ namespace Symfony\Bridge\Doctrine\Tests\Types; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Platforms\MariaDBPlatform; use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\DBAL\Platforms\SqlitePlatform; @@ -140,13 +141,15 @@ public function testGetGuidTypeDeclarationSQL(AbstractPlatform $platform, string $this->assertEquals($expectedDeclaration, $this->type->getSqlDeclaration(['length' => 36], $platform)); } - public static function provideSqlDeclarations(): array + public static function provideSqlDeclarations(): \Generator { - return [ - [new PostgreSQLPlatform(), 'UUID'], - [new SqlitePlatform(), 'BLOB'], - [new MySQLPlatform(), 'BINARY(16)'], - ]; + yield [new PostgreSQLPlatform(), 'UUID']; + yield [new SqlitePlatform(), 'BLOB']; + yield [new MySQLPlatform(), 'BINARY(16)']; + + if (class_exists(MariaDBPlatform::class)) { + yield [new MariaDBPlatform(), 'BINARY(16)']; + } } public function testRequiresSQLCommentHint() diff --git a/src/Symfony/Bridge/Doctrine/Tests/Types/UuidTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Types/UuidTypeTest.php index d49afc5f97ec4..120887ef3653a 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Types/UuidTypeTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Types/UuidTypeTest.php @@ -12,6 +12,7 @@ namespace Symfony\Bridge\Doctrine\Tests\Types; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Platforms\MariaDBPlatform; use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\DBAL\Platforms\SqlitePlatform; @@ -152,13 +153,15 @@ public function testGetGuidTypeDeclarationSQL(AbstractPlatform $platform, string $this->assertEquals($expectedDeclaration, $this->type->getSqlDeclaration(['length' => 36], $platform)); } - public static function provideSqlDeclarations(): array + public static function provideSqlDeclarations(): \Generator { - return [ - [new PostgreSQLPlatform(), 'UUID'], - [new SqlitePlatform(), 'BLOB'], - [new MySQLPlatform(), 'BINARY(16)'], - ]; + yield [new PostgreSQLPlatform(), 'UUID']; + yield [new SqlitePlatform(), 'BLOB']; + yield [new MySQLPlatform(), 'BINARY(16)']; + + if (class_exists(MariaDBPlatform::class)) { + yield [new MariaDBPlatform(), 'BINARY(16)']; + } } public function testRequiresSQLCommentHint() diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php index 3176ac22f9678..b1406d9aaf914 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php @@ -13,11 +13,12 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\DBAL\Types\Type; +use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Mapping\ClassMetadataInfo; use Doctrine\ORM\Tools\SchemaTool; use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\ObjectManager; -use Doctrine\Persistence\ObjectRepository; +use PHPUnit\Framework\MockObject\MockObject; use Symfony\Bridge\Doctrine\Tests\DoctrineTestHelper; use Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity; use Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity2; @@ -58,7 +59,7 @@ class UniqueEntityValidatorTest extends ConstraintValidatorTestCase protected $registry; /** - * @var ObjectRepository + * @var MockObject&EntityRepository */ protected $repository; @@ -95,7 +96,8 @@ protected function createRegistryMock($em = null) protected function createRepositoryMock() { - $repository = $this->getMockBuilder(ObjectRepository::class) + $repository = $this->getMockBuilder(EntityRepository::class) + ->disableOriginalConstructor() ->onlyMethods(['find', 'findAll', 'findOneBy', 'findBy', 'getClassName']) ->addMethods(['findByCustom']) ->getMock() diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/DoctrineLoaderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/DoctrineLoaderTest.php index eb83aecbf5990..e09da3634ac7c 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Validator/DoctrineLoaderTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/DoctrineLoaderTest.php @@ -92,9 +92,6 @@ public function testLoadClassMetadata() $parentClassMetadata = $validator->getMetadataFor(new DoctrineLoaderParentEntity()); - $publicParentMaxLengthMetadata = $parentClassMetadata->getPropertyMetadata('publicParentMaxLength'); - $this->assertCount(0, $publicParentMaxLengthMetadata); - $privateParentMaxLengthMetadata = $parentClassMetadata->getPropertyMetadata('privateParentMaxLength'); $this->assertCount(1, $privateParentMaxLengthMetadata); $privateParentMaxLengthConstraints = $privateParentMaxLengthMetadata[0]->getConstraints(); diff --git a/src/Symfony/Bridge/Doctrine/composer.json b/src/Symfony/Bridge/Doctrine/composer.json index abfa858cf037f..2a8a509b90c17 100644 --- a/src/Symfony/Bridge/Doctrine/composer.json +++ b/src/Symfony/Bridge/Doctrine/composer.json @@ -39,7 +39,7 @@ "symfony/security-core": "^6.0", "symfony/expression-language": "^5.4|^6.0", "symfony/uid": "^5.4|^6.0", - "symfony/validator": "^5.4|^6.0", + "symfony/validator": "^5.4.25|~6.2.12|^6.3.1", "symfony/translation": "^5.4|^6.0", "symfony/var-dumper": "^5.4|^6.0", "doctrine/annotations": "^1.10.4|^2", @@ -61,7 +61,7 @@ "symfony/property-info": "<5.4", "symfony/security-bundle": "<5.4", "symfony/security-core": "<6.0", - "symfony/validator": "<5.4" + "symfony/validator": "<5.4.25|>=6,<6.2.12|>=6.3,<6.3.1" }, "suggest": { "symfony/form": "", diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php index f28933cf97357..5a1ba44ef0010 100644 --- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php +++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php @@ -368,7 +368,7 @@ class_exists(\SymfonyExcludeListSimplePhpunit::class, false) && PHPUnit\Util\Bla } } -$cmd[0] = sprintf('%s %s --colors=always', $PHP, escapeshellarg("$PHPUNIT_DIR/$PHPUNIT_VERSION_DIR/phpunit")); +$cmd[0] = sprintf('%s %s --colors=%s', $PHP, escapeshellarg("$PHPUNIT_DIR/$PHPUNIT_VERSION_DIR/phpunit"), false === $getEnvVar('NO_COLOR') ? 'always' : 'never'); $cmd = str_replace('%', '%%', implode(' ', $cmd)).' %1$s'; if ('\\' === \DIRECTORY_SEPARATOR) { @@ -435,7 +435,7 @@ class SymfonyExcludeListSimplePhpunit { } } - array_splice($argv, 1, 0, ['--colors=always']); + array_splice($argv, 1, 0, ['--colors='.(false === $getEnvVar('NO_COLOR') ? 'always' : 'never')]); $_SERVER['argv'] = $argv; $_SERVER['argc'] = ++$argc; include "$PHPUNIT_DIR/$PHPUNIT_VERSION_DIR/phpunit"; diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php index f39950587c3e8..3ce96a1316d8c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php @@ -101,6 +101,10 @@ private static function formatFileSize(string $path): string if (is_file($path)) { $size = filesize($path) ?: 0; } else { + if (!is_dir($path)) { + return 'n/a'; + } + $size = 0; foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS | \RecursiveDirectoryIterator::FOLLOW_SYMLINKS)) as $file) { if ($file->isReadable()) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AbstractConfigCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AbstractConfigCommand.php index 5f79d95bf036d..6ca739a7ed6e0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/AbstractConfigCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/AbstractConfigCommand.php @@ -54,6 +54,44 @@ protected function listBundles(OutputInterface|StyleInterface $output) } } + protected function listNonBundleExtensions(OutputInterface|StyleInterface $output): void + { + $title = 'Available registered non-bundle extension aliases'; + $headers = ['Extension alias']; + $rows = []; + + $kernel = $this->getApplication()->getKernel(); + + $bundleExtensions = []; + foreach ($kernel->getBundles() as $bundle) { + if ($extension = $bundle->getContainerExtension()) { + $bundleExtensions[\get_class($extension)] = true; + } + } + + $extensions = $this->getContainerBuilder($kernel)->getExtensions(); + + foreach ($extensions as $alias => $extension) { + if (isset($bundleExtensions[\get_class($extension)])) { + continue; + } + $rows[] = [$alias]; + } + + if (!$rows) { + return; + } + + if ($output instanceof StyleInterface) { + $output->title($title); + $output->table($headers, $rows); + } else { + $output->writeln($title); + $table = new Table($output); + $table->setHeaders($headers)->setRows($rows)->render(); + } + } + protected function findExtension(string $name): ExtensionInterface { $bundles = $this->initializeBundles(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/BuildDebugContainerTrait.php b/src/Symfony/Bundle/FrameworkBundle/Command/BuildDebugContainerTrait.php index 5ac0c8cbc2cb1..56166a197fb51 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/BuildDebugContainerTrait.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/BuildDebugContainerTrait.php @@ -50,7 +50,14 @@ protected function getContainerBuilder(KernelInterface $kernel): ContainerBuilde $container->getCompilerPassConfig()->setAfterRemovingPasses([]); $container->compile(); } else { - (new XmlFileLoader($container = new ContainerBuilder(), new FileLocator()))->load($kernel->getContainer()->getParameter('debug.container.dump')); + $buildContainer = \Closure::bind(function () { + $containerBuilder = $this->getContainerBuilder(); + $this->prepareContainer($containerBuilder); + + return $containerBuilder; + }, $kernel, \get_class($kernel)); + $container = $buildContainer(); + (new XmlFileLoader($container, new FileLocator()))->load($kernel->getContainer()->getParameter('debug.container.dump')); $locatorPass = new ServiceLocatorTagPass(); $locatorPass->process($container); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php index c5078d1b5b401..41321bffd3f38 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php @@ -71,14 +71,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if (null === $name = $input->getArgument('name')) { $this->listBundles($errorIo); - - $kernel = $this->getApplication()->getKernel(); - if ($kernel instanceof ExtensionInterface - && ($kernel instanceof ConfigurationInterface || $kernel instanceof ConfigurationExtensionInterface) - && $kernel->getAlias() - ) { - $errorIo->table(['Kernel Extension'], [[$kernel->getAlias()]]); - } + $this->listNonBundleExtensions($errorIo); $errorIo->comment('Provide the name of a bundle as the first argument of this command to dump its configuration. (e.g. debug:config FrameworkBundle)'); $errorIo->comment('For dumping a specific option, add its path as the second argument of this command. (e.g. debug:config FrameworkBundle serializer to dump the framework.serializer configuration)'); @@ -167,12 +160,12 @@ private function getConfigForExtension(ExtensionInterface $extension, ContainerB // Fall back to default config if the extension has one - if (!$extension instanceof ConfigurationExtensionInterface) { + if (!$extension instanceof ConfigurationExtensionInterface && !$extension instanceof ConfigurationInterface) { throw new \LogicException(sprintf('The extension with alias "%s" does not have configuration.', $extensionAlias)); } $configs = $container->getExtensionConfig($extensionAlias); - $configuration = $extension->getConfiguration($configs, $container); + $configuration = $extension instanceof ConfigurationInterface ? $extension : $extension->getConfiguration($configs, $container); $this->validateConfiguration($extension, $configuration); return (new Processor())->processConfiguration($configuration, $configs); @@ -181,7 +174,8 @@ private function getConfigForExtension(ExtensionInterface $extension, ContainerB public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { if ($input->mustSuggestArgumentValuesFor('name')) { - $suggestions->suggestValues($this->getAvailableBundles(!preg_match('/^[A-Z]/', $input->getCompletionValue()))); + $suggestions->suggestValues($this->getAvailableExtensions()); + $suggestions->suggestValues($this->getAvailableBundles()); return; } @@ -196,11 +190,23 @@ public function complete(CompletionInput $input, CompletionSuggestions $suggesti } } - private function getAvailableBundles(bool $alias): array + private function getAvailableExtensions(): array + { + $kernel = $this->getApplication()->getKernel(); + + $extensions = []; + foreach ($this->getContainerBuilder($kernel)->getExtensions() as $alias => $extension) { + $extensions[] = $alias; + } + + return $extensions; + } + + private function getAvailableBundles(): array { $availableBundles = []; foreach ($this->getApplication()->getKernel()->getBundles() as $bundle) { - $availableBundles[] = $alias ? $bundle->getContainerExtension()->getAlias() : $bundle->getName(); + $availableBundles[] = $bundle->getName(); } return $availableBundles; diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php index c28c5a3d0de7a..60510787af444 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php @@ -23,8 +23,6 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -use Symfony\Component\DependencyInjection\Extension\ConfigurationExtensionInterface; -use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; use Symfony\Component\Yaml\Yaml; /** @@ -81,14 +79,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if (null === $name = $input->getArgument('name')) { $this->listBundles($errorIo); - - $kernel = $this->getApplication()->getKernel(); - if ($kernel instanceof ExtensionInterface - && ($kernel instanceof ConfigurationInterface || $kernel instanceof ConfigurationExtensionInterface) - && $kernel->getAlias() - ) { - $errorIo->table(['Kernel Extension'], [[$kernel->getAlias()]]); - } + $this->listNonBundleExtensions($errorIo); $errorIo->comment([ 'Provide the name of a bundle as the first argument of this command to dump its default configuration. (e.g. config:dump-reference FrameworkBundle)', @@ -156,6 +147,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { if ($input->mustSuggestArgumentValuesFor('name')) { + $suggestions->suggestValues($this->getAvailableExtensions()); $suggestions->suggestValues($this->getAvailableBundles()); } @@ -164,13 +156,24 @@ public function complete(CompletionInput $input, CompletionSuggestions $suggesti } } + private function getAvailableExtensions(): array + { + $kernel = $this->getApplication()->getKernel(); + + $extensions = []; + foreach ($this->getContainerBuilder($kernel)->getExtensions() as $alias => $extension) { + $extensions[] = $alias; + } + + return $extensions; + } + private function getAvailableBundles(): array { $bundles = []; foreach ($this->getApplication()->getKernel()->getBundles() as $bundle) { $bundles[] = $bundle->getName(); - $bundles[] = $bundle->getContainerExtension()->getAlias(); } return $bundles; diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php index c454b85ffb4bf..5f0588c9a6d48 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php @@ -106,6 +106,9 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o if ($service instanceof Alias) { $data['aliases'][$serviceId] = $this->getContainerAliasData($service); } elseif ($service instanceof Definition) { + if ($service->hasTag('container.excluded')) { + continue; + } $data['definitions'][$serviceId] = $this->getContainerDefinitionData($service, $omitTags, $showArguments, $builder, $serviceId); } else { $data['services'][$serviceId] = $service::class; diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php index 36b297172681f..836f06742cd6b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php @@ -162,6 +162,9 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o if ($service instanceof Alias) { $services['aliases'][$serviceId] = $service; } elseif ($service instanceof Definition) { + if ($service->hasTag('container.excluded')) { + continue; + } $services['definitions'][$serviceId] = $service; } else { $services['services'][$serviceId] = $service; diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php index e589f7b3400a8..8f6ef7e9a420e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php @@ -198,6 +198,10 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o } if ($definition instanceof Definition) { + if ($definition->hasTag('container.excluded')) { + unset($serviceIds[$key]); + continue; + } if ($showTag) { $tags = $definition->getTag($showTag); foreach ($tags as $tag) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php index 6c01c15ce04db..ecb9b01b776a7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php @@ -294,6 +294,10 @@ private function getContainerServicesDocument(ContainerBuilder $builder, string continue; } + if ($service instanceof Definition && $service->hasTag('container.excluded')) { + continue; + } + $serviceXML = $this->getContainerServiceDocument($service, $serviceId, null, $showArguments); $containerXML->appendChild($containerXML->ownerDocument->importNode($serviceXML->childNodes->item(0), true)); } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php index f127f32b49952..1dcee2d998a56 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php @@ -81,7 +81,6 @@ class UnusedTagsPass implements CompilerPassInterface 'routing.route_loader', 'security.authenticator.login_linker', 'security.expression_language_provider', - 'security.remember_me_aware', 'security.remember_me_handler', 'security.voter', 'serializer.encoder', diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/ContainerExcluded.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/ContainerExcluded.php new file mode 100644 index 0000000000000..2508b74276bc0 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/ContainerExcluded.php @@ -0,0 +1,7 @@ +createCommandTester($debug); + $ret = $tester->execute([]); + + $this->assertSame(0, $ret, 'Returns 0 in case of success'); + $this->assertStringContainsString('Available registered bundles with their extension alias if available', $tester->getDisplay()); + $this->assertStringContainsString(' DefaultConfigTestBundle default_config_test', $tester->getDisplay()); + $this->assertStringContainsString(' ExtensionWithoutConfigTestBundle extension_without_config_test', $tester->getDisplay()); + $this->assertStringContainsString(' FrameworkBundle framework', $tester->getDisplay()); + $this->assertStringContainsString(' TestBundle test', $tester->getDisplay()); + $this->assertStringContainsString('Available registered non-bundle extension aliases', $tester->getDisplay()); + $this->assertStringContainsString(' foo', $tester->getDisplay()); + $this->assertStringContainsString(' test_dump', $tester->getDisplay()); + } - protected function setUp(): void + /** + * @testWith [true] + * [false] + */ + public function testDumpKernelExtension(bool $debug) { - $kernel = static::createKernel(['test_case' => 'ConfigDump', 'root_config' => 'config.yml']); - $this->application = new Application($kernel); - $this->application->doRun(new ArrayInput([]), new NullOutput()); + $tester = $this->createCommandTester($debug); + $ret = $tester->execute(['name' => 'foo']); + + $this->assertSame(0, $ret, 'Returns 0 in case of success'); + $this->assertStringContainsString('foo:', $tester->getDisplay()); + $this->assertStringContainsString(' foo: bar', $tester->getDisplay()); } - public function testDumpBundleName() + /** + * @testWith [true] + * [false] + */ + public function testDumpBundleName(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute(['name' => 'TestBundle']); $this->assertSame(0, $ret, 'Returns 0 in case of success'); $this->assertStringContainsString('custom: foo', $tester->getDisplay()); } - public function testDumpBundleOption() + /** + * @testWith [true] + * [false] + */ + public function testDumpBundleOption(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute(['name' => 'TestBundle', 'path' => 'custom']); $this->assertSame(0, $ret, 'Returns 0 in case of success'); $this->assertStringContainsString('foo', $tester->getDisplay()); } - public function testParametersValuesAreResolved() + /** + * @testWith [true] + * [false] + */ + public function testParametersValuesAreResolved(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute(['name' => 'framework']); $this->assertSame(0, $ret, 'Returns 0 in case of success'); @@ -60,9 +97,13 @@ public function testParametersValuesAreResolved() $this->assertStringContainsString('secret: test', $tester->getDisplay()); } - public function testParametersValuesAreFullyResolved() + /** + * @testWith [true] + * [false] + */ + public function testParametersValuesAreFullyResolved(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute(['name' => 'framework', '--resolve-env' => true]); $this->assertSame(0, $ret, 'Returns 0 in case of success'); @@ -72,77 +113,105 @@ public function testParametersValuesAreFullyResolved() $this->assertStringContainsString('ide: '.($_ENV['SYMFONY_IDE'] ?? $_SERVER['SYMFONY_IDE'] ?? 'null'), $tester->getDisplay()); } - public function testDefaultParameterValueIsResolvedIfConfigIsExisting() + /** + * @testWith [true] + * [false] + */ + public function testDefaultParameterValueIsResolvedIfConfigIsExisting(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute(['name' => 'framework']); $this->assertSame(0, $ret, 'Returns 0 in case of success'); - $kernelCacheDir = $this->application->getKernel()->getContainer()->getParameter('kernel.cache_dir'); + $kernelCacheDir = self::$kernel->getContainer()->getParameter('kernel.cache_dir'); $this->assertStringContainsString(sprintf("dsn: 'file:%s/profiler'", $kernelCacheDir), $tester->getDisplay()); } - public function testDumpExtensionConfigWithoutBundle() + /** + * @testWith [true] + * [false] + */ + public function testDumpExtensionConfigWithoutBundle(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute(['name' => 'test_dump']); $this->assertSame(0, $ret, 'Returns 0 in case of success'); $this->assertStringContainsString('enabled: true', $tester->getDisplay()); } - public function testDumpUndefinedBundleOption() + /** + * @testWith [true] + * [false] + */ + public function testDumpUndefinedBundleOption(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $tester->execute(['name' => 'TestBundle', 'path' => 'foo']); $this->assertStringContainsString('Unable to find configuration for "test.foo"', $tester->getDisplay()); } - public function testDumpWithPrefixedEnv() + /** + * @testWith [true] + * [false] + */ + public function testDumpWithPrefixedEnv(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $tester->execute(['name' => 'FrameworkBundle']); $this->assertStringContainsString("cookie_httponly: '%env(bool:COOKIE_HTTPONLY)%'", $tester->getDisplay()); } - public function testDumpFallsBackToDefaultConfigAndResolvesParameterValue() + /** + * @testWith [true] + * [false] + */ + public function testDumpFallsBackToDefaultConfigAndResolvesParameterValue(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute(['name' => 'DefaultConfigTestBundle']); $this->assertSame(0, $ret, 'Returns 0 in case of success'); $this->assertStringContainsString('foo: bar', $tester->getDisplay()); } - public function testDumpFallsBackToDefaultConfigAndResolvesEnvPlaceholder() + /** + * @testWith [true] + * [false] + */ + public function testDumpFallsBackToDefaultConfigAndResolvesEnvPlaceholder(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute(['name' => 'DefaultConfigTestBundle']); $this->assertSame(0, $ret, 'Returns 0 in case of success'); $this->assertStringContainsString("baz: '%env(BAZ)%'", $tester->getDisplay()); } - public function testDumpThrowsExceptionWhenDefaultConfigFallbackIsImpossible() + /** + * @testWith [true] + * [false] + */ + public function testDumpThrowsExceptionWhenDefaultConfigFallbackIsImpossible(bool $debug) { $this->expectException(\LogicException::class); $this->expectExceptionMessage('The extension with alias "extension_without_config_test" does not have configuration.'); - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $tester->execute(['name' => 'ExtensionWithoutConfigTestBundle']); } /** * @dataProvider provideCompletionSuggestions */ - public function testComplete(array $input, array $expectedSuggestions) + public function testComplete(bool $debug, array $input, array $expectedSuggestions) { - $this->application->add(new ConfigDebugCommand()); - - $tester = new CommandCompletionTester($this->application->get('debug:config')); + $application = $this->createApplication($debug); + $application->add(new ConfigDebugCommand()); + $tester = new CommandCompletionTester($application->get('debug:config')); $suggestions = $tester->complete($input); foreach ($expectedSuggestions as $expectedSuggestion) { @@ -152,17 +221,28 @@ public function testComplete(array $input, array $expectedSuggestions) public static function provideCompletionSuggestions(): \Generator { - yield 'name' => [[''], ['default_config_test', 'extension_without_config_test', 'framework', 'test']]; - - yield 'name (started CamelCase)' => [['Fra'], ['DefaultConfigTestBundle', 'ExtensionWithoutConfigTestBundle', 'FrameworkBundle', 'TestBundle']]; + $name = ['default_config_test', 'extension_without_config_test', 'framework', 'test', 'foo', 'test_dump']; + yield 'name, no debug' => [false, [''], $name]; + yield 'name, debug' => [true, [''], $name]; - yield 'name with existing path' => [['framework', ''], ['secret', 'router.resource', 'router.utf8', 'router.enabled', 'validation.enabled', 'default_locale']]; + $nameWithPath = ['secret', 'router.resource', 'router.utf8', 'router.enabled', 'validation.enabled', 'default_locale']; + yield 'name with existing path, no debug' => [false, ['framework', ''], $nameWithPath]; + yield 'name with existing path, debug' => [true, ['framework', ''], $nameWithPath]; } - private function createCommandTester(): CommandTester + private function createCommandTester(bool $debug): CommandTester { - $command = $this->application->find('debug:config'); + $command = $this->createApplication($debug)->find('debug:config'); return new CommandTester($command); } + + private function createApplication(bool $debug): Application + { + $kernel = static::bootKernel(['debug' => $debug, 'test_case' => 'ConfigDump', 'root_config' => 'config.yml']); + $application = new Application($kernel); + $application->doRun(new ArrayInput([]), new NullOutput()); + + return $application; + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ConfigDumpReferenceCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ConfigDumpReferenceCommandTest.php index e944960aea4e7..8f5930faac2eb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ConfigDumpReferenceCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ConfigDumpReferenceCommandTest.php @@ -23,26 +23,47 @@ */ class ConfigDumpReferenceCommandTest extends AbstractWebTestCase { - private $application; - - protected function setUp(): void + /** + * @testWith [true] + * [false] + */ + public function testShowList(bool $debug) { - $kernel = static::createKernel(['test_case' => 'ConfigDump', 'root_config' => 'config.yml']); - $this->application = new Application($kernel); - $this->application->doRun(new ArrayInput([]), new NullOutput()); + $tester = $this->createCommandTester($debug); + $ret = $tester->execute([]); + + $this->assertSame(0, $ret, 'Returns 0 in case of success'); + $this->assertStringContainsString('Available registered bundles with their extension alias if available', $tester->getDisplay()); + $this->assertStringContainsString(' DefaultConfigTestBundle default_config_test', $tester->getDisplay()); + $this->assertStringContainsString(' ExtensionWithoutConfigTestBundle extension_without_config_test', $tester->getDisplay()); + $this->assertStringContainsString(' FrameworkBundle framework', $tester->getDisplay()); + $this->assertStringContainsString(' TestBundle test', $tester->getDisplay()); + $this->assertStringContainsString('Available registered non-bundle extension aliases', $tester->getDisplay()); + $this->assertStringContainsString(' foo', $tester->getDisplay()); + $this->assertStringContainsString(' test_dump', $tester->getDisplay()); } - public function testDumpKernelExtension() + /** + * @testWith [true] + * [false] + */ + public function testDumpKernelExtension(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute(['name' => 'foo']); + + $this->assertSame(0, $ret, 'Returns 0 in case of success'); $this->assertStringContainsString('foo:', $tester->getDisplay()); $this->assertStringContainsString(' bar', $tester->getDisplay()); } - public function testDumpBundleName() + /** + * @testWith [true] + * [false] + */ + public function testDumpBundleName(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute(['name' => 'TestBundle']); $this->assertSame(0, $ret, 'Returns 0 in case of success'); @@ -50,18 +71,26 @@ public function testDumpBundleName() $this->assertStringContainsString(' custom:', $tester->getDisplay()); } - public function testDumpExtensionConfigWithoutBundle() + /** + * @testWith [true] + * [false] + */ + public function testDumpExtensionConfigWithoutBundle(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute(['name' => 'test_dump']); $this->assertSame(0, $ret, 'Returns 0 in case of success'); $this->assertStringContainsString('enabled: true', $tester->getDisplay()); } - public function testDumpAtPath() + /** + * @testWith [true] + * [false] + */ + public function testDumpAtPath(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute([ 'name' => 'test', 'path' => 'array', @@ -79,9 +108,13 @@ public function testDumpAtPath() , $tester->getDisplay(true)); } - public function testDumpAtPathXml() + /** + * @testWith [true] + * [false] + */ + public function testDumpAtPathXml(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute([ 'name' => 'test', 'path' => 'array', @@ -95,24 +128,40 @@ public function testDumpAtPathXml() /** * @dataProvider provideCompletionSuggestions */ - public function testComplete(array $input, array $expectedSuggestions) + public function testComplete(bool $debug, array $input, array $expectedSuggestions) { - $this->application->add(new ConfigDumpReferenceCommand()); - $tester = new CommandCompletionTester($this->application->get('config:dump-reference')); - $suggestions = $tester->complete($input, 2); + $application = $this->createApplication($debug); + + $application->add(new ConfigDumpReferenceCommand()); + $tester = new CommandCompletionTester($application->get('config:dump-reference')); + $suggestions = $tester->complete($input); $this->assertSame($expectedSuggestions, $suggestions); } public static function provideCompletionSuggestions(): iterable { - yield 'name' => [[''], ['DefaultConfigTestBundle', 'default_config_test', 'ExtensionWithoutConfigTestBundle', 'extension_without_config_test', 'FrameworkBundle', 'framework', 'TestBundle', 'test']]; - yield 'option --format' => [['--format', ''], ['yaml', 'xml']]; + $name = ['foo', 'default_config_test', 'extension_without_config_test', 'framework', 'test', 'test_dump', 'DefaultConfigTestBundle', 'ExtensionWithoutConfigTestBundle', 'FrameworkBundle', 'TestBundle']; + yield 'name, no debug' => [false, [''], $name]; + yield 'name, debug' => [true, [''], $name]; + + $optionFormat = ['yaml', 'xml']; + yield 'option --format, no debug' => [false, ['--format', ''], $optionFormat]; + yield 'option --format, debug' => [true, ['--format', ''], $optionFormat]; } - private function createCommandTester(): CommandTester + private function createCommandTester(bool $debug): CommandTester { - $command = $this->application->find('config:dump-reference'); + $command = $this->createApplication($debug)->find('config:dump-reference'); return new CommandTester($command); } + + private function createApplication(bool $debug): Application + { + $kernel = static::createKernel(['debug' => $debug, 'test_case' => 'ConfigDump', 'root_config' => 'config.yml']); + $application = new Application($kernel); + $application->doRun(new ArrayInput([]), new NullOutput()); + + return $application; + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php index 67727202e72b7..bb09c6aed0765 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php @@ -13,6 +13,7 @@ use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\BackslashClass; +use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\ContainerExcluded; use Symfony\Component\Console\Tester\ApplicationTester; use Symfony\Component\Console\Tester\CommandCompletionTester; @@ -85,6 +86,19 @@ public function testDeprecatedServiceAndAlias() $this->assertStringContainsString('[WARNING] The "deprecated_alias" alias is deprecated since foo/bar 1.9 and will be removed in 2.0', $tester->getDisplay()); } + public function testExcludedService() + { + static::bootKernel(['test_case' => 'ContainerDebug', 'root_config' => 'config.yml']); + + $application = new Application(static::$kernel); + $application->setAutoExit(false); + + $tester = new ApplicationTester($application); + + $tester->run(['command' => 'debug:container']); + $this->assertStringNotContainsString(ContainerExcluded::class, $tester->getDisplay()); + } + /** * @dataProvider provideIgnoreBackslashWhenFindingService */ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/config.yml index cc1a01bb8f0b5..15ddaca64356f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/config.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/config.yml @@ -33,3 +33,5 @@ services: - '%env(REAL)%' - '%env(int:key:2:json:JSON)%' - '%env(float:key:2:json:JSON)%' + Symfony\Bundle\FrameworkBundle\Tests\Fixtures\ContainerExcluded: + tags: ['container.excluded'] diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php index d3ec4633cf5ce..eac593b8c7a6c 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php @@ -111,15 +111,6 @@ public function createAuthenticator(ContainerBuilder $container, string $firewal ->replaceArgument(3, $config['name'] ?? $this->options['name']) ; - foreach ($container->findTaggedServiceIds('security.remember_me_aware') as $serviceId => $attributes) { - // register ContextListener - if (str_starts_with($serviceId, 'security.context_listener')) { - continue; - } - - throw new \LogicException(sprintf('Symfony Authenticator Security dropped support for the "security.remember_me_aware" tag, service "%s" will no longer work as expected.', $serviceId)); - } - return $authenticatorId; } diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index cd514bb44367d..e6d26f8789769 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -417,11 +417,10 @@ private function createFirewall(ContainerBuilder $container, string $id, array $ $listeners[] = new Reference('security.channel_listener'); $contextKey = null; - $contextListenerId = null; // Context serializer listener if (false === $firewall['stateless']) { $contextKey = $firewall['context'] ?? $id; - $listeners[] = new Reference($contextListenerId = $this->createContextListener($container, $contextKey, $firewallEventDispatcherId)); + $listeners[] = new Reference($this->createContextListener($container, $contextKey, $firewallEventDispatcherId)); $sessionStrategyId = 'security.authentication.session_strategy'; $container @@ -490,7 +489,7 @@ private function createFirewall(ContainerBuilder $container, string $id, array $ // Authentication listeners $firewallAuthenticationProviders = []; - [$authListeners, $defaultEntryPoint] = $this->createAuthenticationListeners($container, $id, $firewall, $firewallAuthenticationProviders, $defaultProvider, $providerIds, $configuredEntryPoint, $contextListenerId); + [$authListeners, $defaultEntryPoint] = $this->createAuthenticationListeners($container, $id, $firewall, $firewallAuthenticationProviders, $defaultProvider, $providerIds, $configuredEntryPoint); // $configuredEntryPoint is resolved into a service ID and stored in $defaultEntryPoint $configuredEntryPoint = $defaultEntryPoint; @@ -596,7 +595,7 @@ private function createContextListener(ContainerBuilder $container, string $cont return $this->contextListeners[$contextKey] = $listenerId; } - private function createAuthenticationListeners(ContainerBuilder $container, string $id, array $firewall, array &$authenticationProviders, ?string $defaultProvider, array $providerIds, ?string $defaultEntryPoint, string $contextListenerId = null) + private function createAuthenticationListeners(ContainerBuilder $container, string $id, array $firewall, array &$authenticationProviders, ?string $defaultProvider, array $providerIds, ?string $defaultEntryPoint) { $listeners = []; $entryPoints = []; @@ -605,7 +604,7 @@ private function createAuthenticationListeners(ContainerBuilder $container, stri $key = str_replace('-', '_', $factory->getKey()); if (isset($firewall[$key])) { - $userProvider = $this->getUserProvider($container, $id, $firewall, $key, $defaultProvider, $providerIds, $contextListenerId); + $userProvider = $this->getUserProvider($container, $id, $firewall, $key, $defaultProvider, $providerIds); if (!$factory instanceof AuthenticatorFactoryInterface) { throw new InvalidConfigurationException(sprintf('Authenticator factory "%s" ("%s") must implement "%s".', get_debug_type($factory), $key, AuthenticatorFactoryInterface::class)); @@ -637,7 +636,7 @@ private function createAuthenticationListeners(ContainerBuilder $container, stri return [$listeners, $defaultEntryPoint]; } - private function getUserProvider(ContainerBuilder $container, string $id, array $firewall, string $factoryKey, ?string $defaultProvider, array $providerIds, ?string $contextListenerId): string + private function getUserProvider(ContainerBuilder $container, string $id, array $firewall, string $factoryKey, ?string $defaultProvider, array $providerIds): string { if (isset($firewall[$factoryKey]['provider'])) { if (!isset($providerIds[$normalizedName = str_replace('-', '_', $firewall[$factoryKey]['provider'])])) { @@ -647,10 +646,6 @@ private function getUserProvider(ContainerBuilder $container, string $id, array return $providerIds[$normalizedName]; } - if ('remember_me' === $factoryKey && $contextListenerId) { - $container->getDefinition($contextListenerId)->addTag('security.remember_me_aware', ['id' => $id, 'provider' => 'none']); - } - if ($defaultProvider) { return $defaultProvider; } diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig index d1d4f0f426ed2..4bb9cb8d1e269 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig @@ -258,6 +258,11 @@ div.sf-toolbar .sf-toolbar-block .sf-toolbar-info-piece.sf-toolbar-info-php-ext position: absolute; } +.sf-toolbar-block.sf-toolbar-block-right .sf-toolbar-info { + border-bottom-left-radius: 4px; + border-bottom-right-radius: 0; +} + .sf-toolbar-block .sf-toolbar-info:empty { visibility: hidden; } @@ -273,9 +278,10 @@ div.sf-toolbar .sf-toolbar-block .sf-toolbar-info-piece.sf-toolbar-info-php-ext text-align: center; } -.sf-toolbar-block .sf-toolbar-status-green, -.sf-toolbar-block .sf-toolbar-info .sf-toolbar-status-green { +.sf-toolbar-block .sf-toolbar-status.sf-toolbar-status-green, +.sf-toolbar-block .sf-toolbar-info .sf-toolbar-status.sf-toolbar-status-green { background-color: #059669; + color: var(--white); } .sf-toolbar-block .sf-toolbar-status.sf-toolbar-status-red, .sf-toolbar-block .sf-toolbar-info .sf-toolbar-status.sf-toolbar-status-red { @@ -288,10 +294,7 @@ div.sf-toolbar .sf-toolbar-block .sf-toolbar-info-piece.sf-toolbar-info-php-ext color: var(--sf-toolbar-yellow-800); } -.sf-toolbar-block.sf-toolbar-status-green { - background-color: #059669; - color: var(--sf-toolbar-white); -} +.sf-toolbar-block.sf-toolbar-status-green::before, .sf-toolbar-block.sf-toolbar-status-red::before, .sf-toolbar-block.sf-toolbar-status-yellow::before { background: var(--sf-toolbar-yellow-400); @@ -307,6 +310,10 @@ div.sf-toolbar .sf-toolbar-block .sf-toolbar-info-piece.sf-toolbar-info-php-ext .sf-toolbar-block.sf-toolbar-status-red::before { background: var(--sf-toolbar-red-400); } +.sf-toolbar-block.sf-toolbar-status-green::before { + background: var(--sf-toolbar-green-400); +} +.sf-toolbar-block-request.sf-toolbar-block.sf-toolbar-status-green::before, .sf-toolbar-block-request.sf-toolbar-block.sf-toolbar-status-red::before, .sf-toolbar-block-request.sf-toolbar-block.sf-toolbar-status-yellow::before { display: none; @@ -384,7 +391,7 @@ div.sf-toolbar .sf-toolbar-block .sf-toolbar-info-piece.sf-toolbar-info-php-ext box-shadow: 1px 0 0 var(--sf-toolbar-black), inset 0 -1px 0 var(--sf-toolbar-black); } .sf-toolbar-block.sf-toolbar-block-right:hover .sf-toolbar-icon { - box-shadow: -2px 0 0 var(--sf-toolbar-black), inset 0 -2px 0 var(--sf-toolbar-black); + box-shadow: -1px 0 0 var(--sf-toolbar-black), inset 0 -1px 0 var(--sf-toolbar-black); } .sf-toolbar-block-request .sf-toolbar-icon { @@ -409,9 +416,6 @@ div.sf-toolbar .sf-toolbar-block .sf-toolbar-info-piece.sf-toolbar-info-php-ext .sf-toolbar-block.sf-toolbar-block-sf-cli .sf-toolbar-label { margin-left: 0; } -.sf-toolbar-block.sf-toolbar-block-sf-cli:hover .sf-toolbar-icon { - box-shadow: 2px 0 0 var(--sf-toolbar-black), inset 0 -2px 0 var(--sf-toolbar-black); -} .sf-toolbar-block:hover, .sf-toolbar-block.hover { @@ -538,7 +542,8 @@ div.sf-toolbar .sf-toolbar-block .sf-toolbar-info-piece.sf-toolbar-info-php-ext .sf-toolbar-icon .sf-toolbar-value { display: none; } -.sf-toolbar-block-config .sf-toolbar-icon .sf-toolbar-label { +.sf-toolbar-block-config .sf-toolbar-icon .sf-toolbar-label, +.sf-cli .sf-toolbar-icon .sf-toolbar-label { display: inline-block; } diff --git a/src/Symfony/Component/Cache/Adapter/DoctrineDbalAdapter.php b/src/Symfony/Component/Cache/Adapter/DoctrineDbalAdapter.php index 25f85d7728844..2c07c3a38ca82 100644 --- a/src/Symfony/Component/Cache/Adapter/DoctrineDbalAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/DoctrineDbalAdapter.php @@ -11,13 +11,17 @@ namespace Symfony\Component\Cache\Adapter; +use Doctrine\DBAL\ArrayParameterType; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Driver\ServerInfoAwareConnection; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Exception as DBALException; use Doctrine\DBAL\Exception\TableNotFoundException; use Doctrine\DBAL\ParameterType; +use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory; use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Tools\DsnParser; use Symfony\Component\Cache\Exception\InvalidArgumentException; use Symfony\Component\Cache\Marshaller\DefaultMarshaller; use Symfony\Component\Cache\Marshaller\MarshallerInterface; @@ -66,7 +70,28 @@ public function __construct(Connection|string $connOrDsn, string $namespace = '' if (!class_exists(DriverManager::class)) { throw new InvalidArgumentException(sprintf('Failed to parse the DSN "%s". Try running "composer require doctrine/dbal".', $connOrDsn)); } - $this->conn = DriverManager::getConnection(['url' => $connOrDsn]); + if (class_exists(DsnParser::class)) { + $params = (new DsnParser([ + 'db2' => 'ibm_db2', + 'mssql' => 'pdo_sqlsrv', + 'mysql' => 'pdo_mysql', + 'mysql2' => 'pdo_mysql', + 'postgres' => 'pdo_pgsql', + 'postgresql' => 'pdo_pgsql', + 'pgsql' => 'pdo_pgsql', + 'sqlite' => 'pdo_sqlite', + 'sqlite3' => 'pdo_sqlite', + ]))->parse($connOrDsn); + } else { + $params = ['url' => $connOrDsn]; + } + + $config = new Configuration(); + if (class_exists(DefaultSchemaManagerFactory::class)) { + $config->setSchemaManagerFactory(new DefaultSchemaManagerFactory()); + } + + $this->conn = DriverManager::getConnection($params, $config); } $this->table = $options['db_table'] ?? $this->table; @@ -143,7 +168,7 @@ protected function doFetch(array $ids): iterable $ids, ], [ ParameterType::INTEGER, - Connection::PARAM_STR_ARRAY, + class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY, ])->iterateNumeric(); foreach ($result as $row) { @@ -161,7 +186,7 @@ protected function doFetch(array $ids): iterable $expired, ], [ ParameterType::INTEGER, - Connection::PARAM_STR_ARRAY, + class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY, ]); } } @@ -204,7 +229,7 @@ protected function doDelete(array $ids): bool { $sql = "DELETE FROM $this->table WHERE $this->idCol IN (?)"; try { - $this->conn->executeStatement($sql, [array_values($ids)], [Connection::PARAM_STR_ARRAY]); + $this->conn->executeStatement($sql, [array_values($ids)], [class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY]); } catch (TableNotFoundException) { } @@ -260,35 +285,42 @@ protected function doSave(array $values, int $lifetime): array|bool $stmt = $this->conn->prepare($sql); } - // $id and $data are defined later in the loop. Binding is done by reference, values are read on execution. if ('sqlsrv' === $platformName || 'oci' === $platformName) { - $stmt->bindParam(1, $id); - $stmt->bindParam(2, $id); - $stmt->bindParam(3, $data, ParameterType::LARGE_OBJECT); + $bind = static function ($id, $data) use ($stmt) { + $stmt->bindValue(1, $id); + $stmt->bindValue(2, $id); + $stmt->bindValue(3, $data, ParameterType::LARGE_OBJECT); + $stmt->bindValue(6, $data, ParameterType::LARGE_OBJECT); + }; $stmt->bindValue(4, $lifetime, ParameterType::INTEGER); $stmt->bindValue(5, $now, ParameterType::INTEGER); - $stmt->bindParam(6, $data, ParameterType::LARGE_OBJECT); $stmt->bindValue(7, $lifetime, ParameterType::INTEGER); $stmt->bindValue(8, $now, ParameterType::INTEGER); } elseif (null !== $platformName) { - $stmt->bindParam(1, $id); - $stmt->bindParam(2, $data, ParameterType::LARGE_OBJECT); + $bind = static function ($id, $data) use ($stmt) { + $stmt->bindValue(1, $id); + $stmt->bindValue(2, $data, ParameterType::LARGE_OBJECT); + }; $stmt->bindValue(3, $lifetime, ParameterType::INTEGER); $stmt->bindValue(4, $now, ParameterType::INTEGER); } else { - $stmt->bindParam(1, $data, ParameterType::LARGE_OBJECT); $stmt->bindValue(2, $lifetime, ParameterType::INTEGER); $stmt->bindValue(3, $now, ParameterType::INTEGER); - $stmt->bindParam(4, $id); $insertStmt = $this->conn->prepare($insertSql); - $insertStmt->bindParam(1, $id); - $insertStmt->bindParam(2, $data, ParameterType::LARGE_OBJECT); $insertStmt->bindValue(3, $lifetime, ParameterType::INTEGER); $insertStmt->bindValue(4, $now, ParameterType::INTEGER); + + $bind = static function ($id, $data) use ($stmt, $insertStmt) { + $stmt->bindValue(1, $data, ParameterType::LARGE_OBJECT); + $stmt->bindValue(4, $id); + $insertStmt->bindValue(1, $id); + $insertStmt->bindValue(2, $data, ParameterType::LARGE_OBJECT); + }; } foreach ($values as $id => $data) { + $bind($id, $data); try { $rowCount = $stmt->executeStatement(); } catch (TableNotFoundException) { diff --git a/src/Symfony/Component/Cache/Tests/Adapter/DoctrineDbalAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/DoctrineDbalAdapterTest.php index 3d5bdde897096..5ce8b4c4cc4d4 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/DoctrineDbalAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/DoctrineDbalAdapterTest.php @@ -16,6 +16,7 @@ use Doctrine\DBAL\Driver\AbstractMySQLDriver; use Doctrine\DBAL\Driver\Middleware; use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory; use Doctrine\DBAL\Schema\Schema; use PHPUnit\Framework\SkippedTestSuiteError; use Psr\Cache\CacheItemPoolInterface; @@ -45,12 +46,12 @@ public static function tearDownAfterClass(): void public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface { - return new DoctrineDbalAdapter(DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile]), '', $defaultLifetime); + return new DoctrineDbalAdapter(DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile], $this->getDbalConfig()), '', $defaultLifetime); } public function testConfigureSchemaDecoratedDbalDriver() { - $connection = DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile]); + $connection = DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile], $this->getDbalConfig()); if (!interface_exists(Middleware::class)) { $this->markTestSkipped('doctrine/dbal v2 does not support custom drivers using middleware'); } @@ -60,7 +61,7 @@ public function testConfigureSchemaDecoratedDbalDriver() ->method('wrap') ->willReturn(new DriverWrapper($connection->getDriver())); - $config = new Configuration(); + $config = $this->getDbalConfig(); $config->setMiddlewares([$middleware]); $connection = DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile], $config); @@ -75,7 +76,7 @@ public function testConfigureSchemaDecoratedDbalDriver() public function testConfigureSchema() { - $connection = DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile]); + $connection = DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile], $this->getDbalConfig()); $schema = new Schema(); $adapter = new DoctrineDbalAdapter($connection); @@ -95,7 +96,7 @@ public function testConfigureSchemaDifferentDbalConnection() public function testConfigureSchemaTableExists() { - $connection = DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile]); + $connection = DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile], $this->getDbalConfig()); $schema = new Schema(); $schema->createTable('cache_items'); @@ -154,4 +155,14 @@ private function createConnectionMock() return $connection; } + + private function getDbalConfig() + { + $config = new Configuration(); + if (class_exists(DefaultSchemaManagerFactory::class)) { + $config->setSchemaManagerFactory(new DefaultSchemaManagerFactory()); + } + + return $config; + } } diff --git a/src/Symfony/Component/Cache/Tests/Traits/RedisTraitTest.php b/src/Symfony/Component/Cache/Tests/Traits/RedisTraitTest.php new file mode 100644 index 0000000000000..e7e368b3e829d --- /dev/null +++ b/src/Symfony/Component/Cache/Tests/Traits/RedisTraitTest.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Traits; + +use PHPUnit\Framework\SkippedTestSuiteError; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Cache\Traits\RedisTrait; + +class RedisTraitTest extends TestCase +{ + public static function setUpBeforeClass(): void + { + if (!getenv('REDIS_CLUSTER_HOSTS')) { + throw new SkippedTestSuiteError('REDIS_CLUSTER_HOSTS env var is not defined.'); + } + } + + /** + * @dataProvider provideCreateConnection + */ + public function testCreateConnection(string $dsn, string $expectedClass) + { + if (!class_exists($expectedClass)) { + throw new SkippedTestSuiteError(sprintf('The "%s" class is required.', $expectedClass)); + } + if (!getenv('REDIS_CLUSTER_HOSTS')) { + throw new SkippedTestSuiteError('REDIS_CLUSTER_HOSTS env var is not defined.'); + } + + $mock = self::getObjectForTrait(RedisTrait::class); + $connection = $mock::createConnection($dsn); + + self::assertInstanceOf($expectedClass, $connection); + } + + public static function provideCreateConnection(): array + { + $hosts = array_map(function ($host) { return sprintf('host[%s]', $host); }, explode(' ', getenv('REDIS_CLUSTER_HOSTS'))); + + return [ + [ + sprintf('redis:?%s&redis_cluster=1', $hosts[0]), + 'RedisCluster', + ], + [ + sprintf('redis:?%s&redis_cluster=true', $hosts[0]), + 'RedisCluster', + ], + [ + sprintf('redis:?%s', $hosts[0]), + 'Redis', + ], + [ + 'dsn' => sprintf('redis:?%s', implode('&', \array_slice($hosts, 0, 2))), + 'RedisArray', + ], + ]; + } +} diff --git a/src/Symfony/Component/Cache/Traits/RedisTrait.php b/src/Symfony/Component/Cache/Traits/RedisTrait.php index 4721f0a4d5624..1a82f2e346b99 100644 --- a/src/Symfony/Component/Cache/Traits/RedisTrait.php +++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php @@ -169,6 +169,11 @@ public static function createConnection(string $dsn, array $options = []): \Redi throw new CacheException(sprintf('Redis Sentinel support requires the "predis/predis" package or the "redis" extension v5.2 or higher: "%s".', $dsn)); } + if (isset($params['lazy'])) { + $params['lazy'] = filter_var($params['lazy'], \FILTER_VALIDATE_BOOLEAN); + } + $params['redis_cluster'] = filter_var($params['redis_cluster'], \FILTER_VALIDATE_BOOLEAN); + if ($params['redis_cluster'] && isset($params['redis_sentinel'])) { throw new InvalidArgumentException(sprintf('Cannot use both "redis_cluster" and "redis_sentinel" at the same time: "%s".', $dsn)); } diff --git a/src/Symfony/Component/Clock/MockClock.php b/src/Symfony/Component/Clock/MockClock.php index a189ff54c4dc9..db22e26b3771e 100644 --- a/src/Symfony/Component/Clock/MockClock.php +++ b/src/Symfony/Component/Clock/MockClock.php @@ -49,7 +49,12 @@ public function sleep(float|int $seconds): void public function modify(string $modifier): void { - if (false === $modifiedNow = @$this->now->modify($modifier)) { + try { + $modifiedNow = @$this->now->modify($modifier); + } catch (\DateMalformedStringException) { + $modifiedNow = false; + } + if (false === $modifiedNow) { throw new \InvalidArgumentException(sprintf('Invalid modifier: "%s". Could not modify MockClock.', $modifier)); } diff --git a/src/Symfony/Component/Console/Output/AnsiColorMode.php b/src/Symfony/Component/Console/Output/AnsiColorMode.php index c6cc5c12f04cd..9ee7ca7c084bc 100644 --- a/src/Symfony/Component/Console/Output/AnsiColorMode.php +++ b/src/Symfony/Component/Console/Output/AnsiColorMode.php @@ -26,7 +26,7 @@ enum AnsiColorMode case Ansi4; /* - * 8-bit Ansi colors (240 differents colors + 16 duplicate color codes, ensuring backward compatibility). + * 8-bit Ansi colors (240 different colors + 16 duplicate color codes, ensuring backward compatibility). * Output syntax is: "ESC[38;5;${foreGroundColorcode};48;5;${backGroundColorcode}m" * Should be compatible with most terminals. */ diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index 0a61f46e5b4ac..0adc3f02ec06f 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -58,11 +58,22 @@ public function __construct(bool $throwOnAutowireException = true) $this->defaultArgument = new class() { public $value; public $names; + public $bag; + + public function withValue(\ReflectionParameter $parameter): self + { + $clone = clone $this; + $clone->value = $this->bag->escapeValue($parameter->getDefaultValue()); + + return $clone; + } }; } public function process(ContainerBuilder $container) { + $this->defaultArgument->bag = $container->getParameterBag(); + try { $this->typesClone = clone $this; parent::process($container); @@ -70,6 +81,7 @@ public function process(ContainerBuilder $container) $this->decoratedClass = null; $this->decoratedId = null; $this->methodCalls = null; + $this->defaultArgument->bag = null; $this->defaultArgument->names = null; $this->getPreviousValue = null; $this->decoratedMethodIndex = null; @@ -315,8 +327,7 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a } // specifically pass the default value - $arguments[$index] = clone $this->defaultArgument; - $arguments[$index]->value = $parameter->getDefaultValue(); + $arguments[$index] = $this->defaultArgument->withValue($parameter); continue; } @@ -326,8 +337,7 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a $failureMessage = $this->createTypeNotFoundMessageCallback($ref, sprintf('argument "$%s" of method "%s()"', $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method)); if ($parameter->isDefaultValueAvailable()) { - $value = clone $this->defaultArgument; - $value->value = $parameter->getDefaultValue(); + $value = $this->defaultArgument->withValue($parameter); } elseif (!$parameter->allowsNull()) { throw new AutowiringFailedException($this->currentId, $failureMessage); } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php index 6da7f0852a85c..8e2cd5ceba654 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php @@ -305,6 +305,10 @@ private function checkType(Definition $checkedDefinition, mixed $value, \Reflect if (false === $value) { return; } + } elseif ('true' === $type) { + if (true === $value) { + return; + } } elseif ($reflectionType->isBuiltin()) { $checkFunction = sprintf('is_%s', $type); if ($checkFunction($value)) { diff --git a/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php b/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php index 5f290f91575b0..895c155e07e46 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php @@ -11,6 +11,8 @@ namespace Symfony\Component\DependencyInjection\Compiler; +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\RuntimeException; @@ -23,31 +25,98 @@ */ class DefinitionErrorExceptionPass extends AbstractRecursivePass { - protected function processValue(mixed $value, bool $isRoot = false): mixed + private $erroredDefinitions = []; + private $targetReferences = []; + private $sourceReferences = []; + + /** + * @return void + */ + public function process(ContainerBuilder $container) { - if (!$value instanceof Definition || !$value->hasErrors()) { - return parent::processValue($value, $isRoot); - } + try { + parent::process($container); + + if (!$this->erroredDefinitions) { + return; + } + + $runtimeIds = []; + + foreach ($this->sourceReferences as $id => $sourceIds) { + foreach ($sourceIds as $sourceId => $isRuntime) { + if (!$isRuntime) { + continue 2; + } + } + + unset($this->erroredDefinitions[$id]); + $runtimeIds[$id] = $id; + } + + if (!$this->erroredDefinitions) { + return; + } + + foreach ($this->targetReferences as $id => $targetIds) { + if (!isset($this->sourceReferences[$id]) || isset($runtimeIds[$id]) || isset($this->erroredDefinitions[$id])) { + continue; + } + foreach ($this->targetReferences[$id] as $targetId => $isRuntime) { + foreach ($this->sourceReferences[$id] as $sourceId => $isRuntime) { + if ($sourceId !== $targetId) { + $this->sourceReferences[$targetId][$sourceId] = false; + $this->targetReferences[$sourceId][$targetId] = false; + } + } + } - if ($isRoot && !$value->isPublic()) { - $graph = $this->container->getCompiler()->getServiceReferenceGraph(); - $runtimeException = false; - foreach ($graph->getNode($this->currentId)->getInEdges() as $edge) { - if (!$edge->getValue() instanceof Reference || ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE !== $edge->getValue()->getInvalidBehavior()) { - $runtimeException = false; - break; + unset($this->sourceReferences[$id]); + } + + foreach ($this->erroredDefinitions as $id => $definition) { + if (isset($this->sourceReferences[$id])) { + continue; } - $runtimeException = true; + + // only show the first error so the user can focus on it + $errors = $definition->getErrors(); + + throw new RuntimeException(reset($errors)); } - if ($runtimeException) { - return parent::processValue($value, $isRoot); + } finally { + $this->erroredDefinitions = []; + $this->targetReferences = []; + $this->sourceReferences = []; + } + } + + protected function processValue(mixed $value, bool $isRoot = false): mixed + { + if ($value instanceof ArgumentInterface) { + parent::processValue($value->getValues()); + + return $value; + } + + if ($value instanceof Reference && $this->currentId !== $targetId = (string) $value) { + if (ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE === $value->getInvalidBehavior()) { + $this->sourceReferences[$targetId][$this->currentId] ??= true; + $this->targetReferences[$this->currentId][$targetId] ??= true; + } else { + $this->sourceReferences[$targetId][$this->currentId] = false; + $this->targetReferences[$this->currentId][$targetId] = false; } + + return $value; + } + + if (!$value instanceof Definition || !$value->hasErrors()) { + return parent::processValue($value, $isRoot); } - // only show the first error so the user can focus on it - $errors = $value->getErrors(); - $message = reset($errors); + $this->erroredDefinitions[$this->currentId] = $value; - throw new RuntimeException($message); + return parent::processValue($value); } } diff --git a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php index c1787edfd7d57..e2684cc20ed97 100644 --- a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php +++ b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php @@ -198,7 +198,9 @@ public function getEnv(string $prefix, string $name, \Closure $getEnv): mixed throw new RuntimeException(sprintf('Unsupported env var prefix "%s".', $prefix)); } - return null; + if (!\in_array($prefix, ['string', 'bool', 'not', 'int', 'float'], true)) { + return null; + } } if ('shuffle' === $prefix) { @@ -207,7 +209,7 @@ public function getEnv(string $prefix, string $name, \Closure $getEnv): mixed return $env; } - if (!\is_scalar($env)) { + if (null !== $env && !\is_scalar($env)) { throw new RuntimeException(sprintf('Non-scalar env var "%s" cannot be cast to "%s".', $name, $prefix)); } @@ -222,7 +224,7 @@ public function getEnv(string $prefix, string $name, \Closure $getEnv): mixed } if ('int' === $prefix) { - if (false === $env = filter_var($env, \FILTER_VALIDATE_INT) ?: filter_var($env, \FILTER_VALIDATE_FLOAT)) { + if (null !== $env && false === $env = filter_var($env, \FILTER_VALIDATE_INT) ?: filter_var($env, \FILTER_VALIDATE_FLOAT)) { throw new RuntimeException(sprintf('Non-numeric env var "%s" cannot be cast to int.', $name)); } @@ -230,7 +232,7 @@ public function getEnv(string $prefix, string $name, \Closure $getEnv): mixed } if ('float' === $prefix) { - if (false === $env = filter_var($env, \FILTER_VALIDATE_FLOAT)) { + if (null !== $env && false === $env = filter_var($env, \FILTER_VALIDATE_FLOAT)) { throw new RuntimeException(sprintf('Non-numeric env var "%s" cannot be cast to float.', $name)); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index 5209d6fd1c21b..00017ea3b07bc 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -1174,6 +1174,19 @@ public function testAutowireWithNamedArgs() $this->assertEquals([new TypedReference(A::class, A::class), 'abc'], $container->getDefinition('foo')->getArguments()); } + public function testAutowireDefaultValueParametersLike() + { + $container = new ContainerBuilder(); + + $container->register('foo', ParametersLikeDefaultValue::class) + ->setAutowired(true) + ->setArgument(1, 'ok'); + + (new AutowirePass())->process($container); + + $this->assertSame('%%not%%one%%parameter%%here%%', $container->getDefinition('foo')->getArgument(0)); + } + public function testAutowireAttribute() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php index c169d6c8ca7fc..4a71ad3155a48 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php @@ -32,6 +32,7 @@ use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\FooObject; use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\IntersectionConstructor; use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\UnionConstructor; +use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\UnionConstructorPHP82; use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\Waldo; use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\WaldoFoo; use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\Wobble; @@ -859,6 +860,26 @@ public function testUnionTypePassesWithFalse() $this->addToAssertionCount(1); } + /** + * @requires PHP 8.2 + */ + public function testUnionTypePassesWithTrue() + { + $container = new ContainerBuilder(); + + $container->register('unionTrue', UnionConstructorPHP82::class) + ->setFactory([UnionConstructorPHP82::class, 'createTrue']) + ->setArguments([true]); + + $container->register('unionNull', UnionConstructorPHP82::class) + ->setFactory([UnionConstructorPHP82::class, 'createNull']) + ->setArguments([null]); + + (new CheckTypeDeclarationsPass(true))->process($container); + + $this->addToAssertionCount(1); + } + public function testUnionTypeFailsWithReference() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/DefinitionErrorExceptionPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/DefinitionErrorExceptionPassTest.php index b22b934dd80cd..f02caf0a7f81e 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/DefinitionErrorExceptionPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/DefinitionErrorExceptionPassTest.php @@ -16,6 +16,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Reference; class DefinitionErrorExceptionPassTest extends TestCase { @@ -49,4 +50,25 @@ public function testNoExceptionThrown() $pass->process($container); $this->assertSame($def, $container->getDefinition('foo_service_id')->getArgument(0)); } + + public function testSkipNestedErrors() + { + $container = new ContainerBuilder(); + + $container->register('nested_error', 'stdClass') + ->addError('Things went wrong!'); + + $container->register('bar', 'stdClass') + ->addArgument(new Reference('nested_error')); + + $container->register('foo', 'stdClass') + ->addArgument(new Reference('bar', ContainerBuilder::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE)); + + $pass = new DefinitionErrorExceptionPass(); + $pass->process($container); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Things went wrong!'); + $container->get('foo'); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index c87de498a12ab..abfc15e22c4ad 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -863,8 +863,8 @@ public function testEnvAreNullable() $container->register('foo', 'stdClass') ->setPublic(true) ->setProperties([ - 'fake' => '%env(int:FAKE)%', - ]); + 'fake' => '%env(resolve:FAKE)%', + ]); $container->compile(true); diff --git a/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php b/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php index eddaccab6345b..b5a499bd90533 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php @@ -851,4 +851,22 @@ public static function provideGetEnvUrlPath() ['blog//', 'https://symfony.com/blog//'], ]; } + + /** + * @testWith ["", "string"] + * [false, "bool"] + * [true, "not"] + * [0, "int"] + * [0.0, "float"] + */ + public function testGetEnvCastsNull($expected, string $prefix) + { + $processor = new EnvVarProcessor(new Container()); + + $this->assertSame($expected, $processor->getEnv($prefix, 'default::FOO', static function () use ($processor) { + return $processor->getEnv('default', ':FOO', static function () { + return null; + }); + })); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/UnionConstructorPHP82.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/UnionConstructorPHP82.php new file mode 100644 index 0000000000000..ea0d9e348f388 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/UnionConstructorPHP82.php @@ -0,0 +1,16 @@ +readlink($filename)) { + $this->dumpFile(Path::makeAbsolute($linkTarget, $dir), $content); + + return; + } + if (!is_dir($dir)) { $this->mkdir($dir); } diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php index 7aca5dd25adbd..2c222fd06b2db 100644 --- a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php +++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php @@ -13,6 +13,7 @@ use Symfony\Component\Filesystem\Exception\InvalidArgumentException; use Symfony\Component\Filesystem\Exception\IOException; +use Symfony\Component\Filesystem\Path; /** * Test class for Filesystem. @@ -1087,20 +1088,18 @@ public function testReadBrokenLink() { $this->markAsSkippedIfSymlinkIsMissing(); - if ('\\' === \DIRECTORY_SEPARATOR) { - $this->markTestSkipped('Windows does not support creating "broken" symlinks'); - } - - $file = $this->workspace.'/file'; - $link = $this->workspace.'/link'; + $file = Path::join($this->workspace, 'file'); + $link = Path::join($this->workspace, 'link'); + touch($file); $this->filesystem->symlink($file, $link); + $this->filesystem->remove($file); - $this->assertEquals($file, $this->filesystem->readlink($link)); + $this->assertEquals($file, Path::normalize($this->filesystem->readlink($link))); $this->assertNull($this->filesystem->readlink($link, true)); touch($file); - $this->assertEquals($file, $this->filesystem->readlink($link, true)); + $this->assertEquals($file, Path::normalize($this->filesystem->readlink($link, true))); } public function testReadLinkDefaultPathDoesNotExist() @@ -1601,6 +1600,33 @@ public function testDumpFileOverwritesAnExistingFile() $this->assertStringEqualsFile($filename, 'bar'); } + public function testDumpFileFollowsSymlink() + { + $filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo.txt'; + file_put_contents($filename, 'FOO BAR'); + $linknameA = $this->workspace.\DIRECTORY_SEPARATOR.'bar.txt'; + $linknameB = $this->workspace.\DIRECTORY_SEPARATOR.'baz.txt'; + $this->filesystem->symlink($filename, $linknameA); + $this->filesystem->symlink($linknameA, $linknameB); + + $this->filesystem->dumpFile($linknameB, 'bar'); + + $this->assertFileExists($filename); + $this->assertFileExists($linknameA); + $this->assertFileExists($linknameB); + $this->assertStringEqualsFile($filename, 'bar'); + $this->assertStringEqualsFile($linknameA, 'bar'); + $this->assertStringEqualsFile($linknameB, 'bar'); + + $this->filesystem->remove($filename); + $this->filesystem->dumpFile($linknameB, 'baz'); + + $this->assertFileExists($filename); + $this->assertStringEqualsFile($filename, 'baz'); + $this->assertStringEqualsFile($linknameA, 'baz'); + $this->assertStringEqualsFile($linknameB, 'baz'); + } + public function testDumpFileWithFileScheme() { $scheme = 'file://'; @@ -1674,6 +1700,35 @@ public function testAppendToFileWithResource() } } + public function testAppendToFileFollowsSymlink() + { + $filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo.txt'; + file_put_contents($filename, 'foo'); + $linknameA = $this->workspace.\DIRECTORY_SEPARATOR.'bar.txt'; + $linknameB = $this->workspace.\DIRECTORY_SEPARATOR.'baz.txt'; + $this->filesystem->symlink($filename, $linknameA); + $this->filesystem->symlink($linknameA, $linknameB); + + $this->filesystem->appendToFile($linknameA, 'bar'); + $this->filesystem->appendToFile($linknameB, 'baz'); + + $this->assertFileExists($filename); + $this->assertFileExists($linknameA); + $this->assertFileExists($linknameB); + $this->assertStringEqualsFile($filename, 'foobarbaz'); + $this->assertStringEqualsFile($linknameA, 'foobarbaz'); + $this->assertStringEqualsFile($linknameB, 'foobarbaz'); + + $this->filesystem->remove($filename); + $this->filesystem->appendToFile($linknameB, 'foo'); + $this->filesystem->appendToFile($linknameA, 'bar'); + + $this->assertFileExists($filename); + $this->assertStringEqualsFile($filename, 'foobar'); + $this->assertStringEqualsFile($linknameA, 'foobar'); + $this->assertStringEqualsFile($linknameB, 'foobar'); + } + public function testAppendToFileWithScheme() { $scheme = 'file://'; diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php index 9e87e15d845be..3143599e9c50b 100644 --- a/src/Symfony/Component/HttpClient/CurlHttpClient.php +++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php @@ -459,6 +459,7 @@ private function validateExtraCurlOptions(array $options): void \CURLOPT_TIMEOUT_MS => 'max_duration', \CURLOPT_TIMEOUT => 'max_duration', \CURLOPT_MAXREDIRS => 'max_redirects', + \CURLOPT_POSTREDIR => 'max_redirects', \CURLOPT_PROXY => 'proxy', \CURLOPT_NOPROXY => 'no_proxy', \CURLOPT_SSL_VERIFYPEER => 'verify_peer', diff --git a/src/Symfony/Component/HttpClient/HttpClientTrait.php b/src/Symfony/Component/HttpClient/HttpClientTrait.php index b081eaff24fb5..36118113fab21 100644 --- a/src/Symfony/Component/HttpClient/HttpClientTrait.php +++ b/src/Symfony/Component/HttpClient/HttpClientTrait.php @@ -540,7 +540,7 @@ private static function parseUrl(string $url, array $query = [], array $allowedS } // https://tools.ietf.org/html/rfc3986#section-3.3 - $parts[$part] = preg_replace_callback("#[^-A-Za-z0-9._~!$&/'()[\]*+,;=:@\\\\^`{|}%]++#", function ($m) { return rawurlencode($m[0]); }, $parts[$part]); + $parts[$part] = preg_replace_callback("#[^-A-Za-z0-9._~!$&/'()[\]*+,;=:@{}%]++#", function ($m) { return rawurlencode($m[0]); }, $parts[$part]); } return [ @@ -627,11 +627,7 @@ private static function mergeQueryString(?string $queryString, array $queryArray '%3B' => ';', '%40' => '@', '%5B' => '[', - '%5C' => '\\', '%5D' => ']', - '%5E' => '^', - '%60' => '`', - '%7C' => '|', ]); } diff --git a/src/Symfony/Component/HttpClient/Retry/GenericRetryStrategy.php b/src/Symfony/Component/HttpClient/Retry/GenericRetryStrategy.php index 9d1473c34a9b3..edbf2c066a766 100644 --- a/src/Symfony/Component/HttpClient/Retry/GenericRetryStrategy.php +++ b/src/Symfony/Component/HttpClient/Retry/GenericRetryStrategy.php @@ -102,7 +102,7 @@ public function getDelay(AsyncContext $context, ?string $responseContent, ?Trans $delay = $this->delayMs * $this->multiplier ** $context->getInfo('retry_count'); if ($this->jitter > 0) { - $randomness = $delay * $this->jitter; + $randomness = (int) ($delay * $this->jitter); $delay = $delay + random_int(-$randomness, +$randomness); } diff --git a/src/Symfony/Component/HttpClient/Tests/HttpClientTraitTest.php b/src/Symfony/Component/HttpClient/Tests/HttpClientTraitTest.php index 3d8d7559d9bf4..0eea00faf865a 100644 --- a/src/Symfony/Component/HttpClient/Tests/HttpClientTraitTest.php +++ b/src/Symfony/Component/HttpClient/Tests/HttpClientTraitTest.php @@ -174,12 +174,13 @@ public static function provideParseUrl(): iterable yield [['http:', '//example.com', null, null, null], 'http://Example.coM:80']; yield [['https:', '//xn--dj-kia8a.example.com:8000', '/', null, null], 'https://DÉjà.Example.com:8000/']; yield [[null, null, '/f%20o.o', '?a=b', '#c'], '/f o%2Eo?a=b#c']; + yield [[null, null, '/custom%7C2010-01-01%2000:00:00%7C2023-06-15%2005:50:35', '?a=b', '#c'], '/custom|2010-01-01 00:00:00|2023-06-15 05:50:35?a=b#c']; yield [[null, '//a:b@foo', '/bar', null, null], '//a:b@foo/bar']; yield [[null, '//a:b@foo', '/b{}', null, null], '//a:b@foo/b{}']; yield [['http:', null, null, null, null], 'http:']; yield [['http:', null, 'bar', null, null], 'http:bar']; yield [[null, null, 'bar', '?a=1&c=c', null], 'bar?a=a&b=b', ['b' => null, 'c' => 'c', 'a' => 1]]; - yield [[null, null, 'bar', '?a=b+c&b=b-._~!$%26/%27()[]*%2B%2C;%3D:@%25\\^`%7B|%7D', null], 'bar?a=b+c', ['b' => 'b-._~!$&/\'()[]*+,;=:@%\\^`{|}']]; + yield [[null, null, 'bar', '?a=b+c&b=b-._~!$%26/%27()[]*%2B%2C;%3D:@%25%5C%5E%60%7B%7C%7D', null], 'bar?a=b+c', ['b' => 'b-._~!$&/\'()[]*+,;=:@%\\^`{|}']]; yield [[null, null, 'bar', '?a=b%2B%20c', null], 'bar?a=b+c', ['a' => 'b+ c']]; yield [[null, null, 'bar', '?a[b]=c', null], 'bar', ['a' => ['b' => 'c']]]; yield [[null, null, 'bar', '?a[b[c]=d', null], 'bar?a[b[c]=d', []]; diff --git a/src/Symfony/Component/HttpClient/Tests/Retry/GenericRetryStrategyTest.php b/src/Symfony/Component/HttpClient/Tests/Retry/GenericRetryStrategyTest.php index 79fc37588bd0f..8219bbe57c0a8 100644 --- a/src/Symfony/Component/HttpClient/Tests/Retry/GenericRetryStrategyTest.php +++ b/src/Symfony/Component/HttpClient/Tests/Retry/GenericRetryStrategyTest.php @@ -67,7 +67,7 @@ public function testGetDelay(int $delay, int $multiplier, int $maxDelay, int $pr public static function provideDelay(): iterable { - // delay, multiplier, maxDelay, retries, expectedDelay + // delay, multiplier, maxDelay, previousRetries, expectedDelay yield [1000, 1, 5000, 0, 1000]; yield [1000, 1, 5000, 1, 1000]; yield [1000, 1, 5000, 2, 1000]; @@ -90,13 +90,16 @@ public static function provideDelay(): iterable yield [0, 2, 10000, 1, 0]; } - public function testJitter() + /** + * @dataProvider provideJitter + */ + public function testJitter(float $multiplier, int $previousRetries) { - $strategy = new GenericRetryStrategy([], 1000, 1, 0, 1); + $strategy = new GenericRetryStrategy([], 1000, $multiplier, 0, 1); $min = 2000; $max = 0; for ($i = 0; $i < 50; ++$i) { - $delay = $strategy->getDelay($this->getContext(0, 'GET', 'http://example.com/', 200), null, null); + $delay = $strategy->getDelay($this->getContext($previousRetries, 'GET', 'http://example.com/', 200), null, null); $min = min($min, $delay); $max = max($max, $delay); } @@ -105,6 +108,13 @@ public function testJitter() $this->assertLessThanOrEqual(1000, $min); } + public static function provideJitter(): iterable + { + // multiplier, previousRetries + yield [1, 0]; + yield [1.1, 2]; + } + private function getContext($retryCount, $method, $url, $statusCode): AsyncContext { $passthru = null; diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php index 06429b4ba0e33..d5f523ed71922 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php @@ -325,8 +325,8 @@ public function updateTimestamp(string $sessionId, string $data): bool $updateStmt = $this->pdo->prepare( "UPDATE $this->table SET $this->lifetimeCol = :expiry, $this->timeCol = :time WHERE $this->idCol = :id" ); - $updateStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); - $updateStmt->bindParam(':expiry', $expiry, \PDO::PARAM_INT); + $updateStmt->bindValue(':id', $sessionId, \PDO::PARAM_STR); + $updateStmt->bindValue(':expiry', $expiry, \PDO::PARAM_INT); $updateStmt->bindValue(':time', time(), \PDO::PARAM_INT); $updateStmt->execute(); } catch (\PDOException $e) { diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/SessionHandlerFactory.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/SessionHandlerFactory.php index e390c8feea5cf..800bb42be711e 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/SessionHandlerFactory.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/SessionHandlerFactory.php @@ -11,7 +11,10 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory; +use Doctrine\DBAL\Tools\DsnParser; use Symfony\Component\Cache\Adapter\AbstractAdapter; /** @@ -65,7 +68,15 @@ public static function createHandler(object|string $connection, array $options = if (!class_exists(DriverManager::class)) { throw new \InvalidArgumentException(sprintf('Unsupported DSN "%s". Try running "composer require doctrine/dbal".', $connection)); } - $connection = DriverManager::getConnection(['url' => $connection])->getWrappedConnection(); + $connection[3] = '-'; + $params = class_exists(DsnParser::class) ? (new DsnParser())->parse($connection) : ['url' => $connection]; + $config = new Configuration(); + if (class_exists(DefaultSchemaManagerFactory::class)) { + $config->setSchemaManagerFactory(new DefaultSchemaManagerFactory()); + } + + $connection = DriverManager::getConnection($params, $config); + $connection = method_exists($connection, 'getNativeConnection') ? $connection->getNativeConnection() : $connection->getWrappedConnection(); // no break; case str_starts_with($connection, 'mssql://'): diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 1ad714e6d98a9..b3689455f301a 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -75,11 +75,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '6.2.11'; - public const VERSION_ID = 60211; + public const VERSION = '6.2.12'; + public const VERSION_ID = 60212; public const MAJOR_VERSION = 6; public const MINOR_VERSION = 2; - public const RELEASE_VERSION = 11; + public const RELEASE_VERSION = 12; public const EXTRA_VERSION = ''; public const END_OF_MAINTENANCE = '07/2023'; diff --git a/src/Symfony/Component/Intl/Intl.php b/src/Symfony/Component/Intl/Intl.php index ddf97a418e432..a810adf7407ce 100644 --- a/src/Symfony/Component/Intl/Intl.php +++ b/src/Symfony/Component/Intl/Intl.php @@ -106,7 +106,7 @@ public static function getIcuDataVersion(): string */ public static function getIcuStubVersion(): string { - return '73.1'; + return '73.2'; } /** diff --git a/src/Symfony/Component/Intl/Resources/data/git-info.txt b/src/Symfony/Component/Intl/Resources/data/git-info.txt index c9973ca5da2cd..55d3c90d5a6fb 100644 --- a/src/Symfony/Component/Intl/Resources/data/git-info.txt +++ b/src/Symfony/Component/Intl/Resources/data/git-info.txt @@ -2,6 +2,6 @@ Git information =============== URL: https://github.com/unicode-org/icu.git -Revision: 5861e1fd52f1d7673eee38bc3c965aa18b336062 +Revision: 680f521746a3bd6a86f25f25ee50a62d88b489cf Author: Peter Edberg -Date: 2023-04-11T10:32:35-07:00 +Date: 2023-06-07T19:19:55-07:00 diff --git a/src/Symfony/Component/Intl/Resources/data/version.txt b/src/Symfony/Component/Intl/Resources/data/version.txt index 62bf7fe234f35..ae6f4d78d2129 100644 --- a/src/Symfony/Component/Intl/Resources/data/version.txt +++ b/src/Symfony/Component/Intl/Resources/data/version.txt @@ -1 +1 @@ -73.1 +73.2 diff --git a/src/Symfony/Component/Lock/Store/DoctrineDbalPostgreSqlStore.php b/src/Symfony/Component/Lock/Store/DoctrineDbalPostgreSqlStore.php index a5fc509528249..2b18cb1c83002 100644 --- a/src/Symfony/Component/Lock/Store/DoctrineDbalPostgreSqlStore.php +++ b/src/Symfony/Component/Lock/Store/DoctrineDbalPostgreSqlStore.php @@ -11,9 +11,12 @@ namespace Symfony\Component\Lock\Store; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Connection; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; +use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory; +use Doctrine\DBAL\Tools\DsnParser; use Symfony\Component\Lock\BlockingSharedLockStoreInterface; use Symfony\Component\Lock\BlockingStoreInterface; use Symfony\Component\Lock\Exception\InvalidArgumentException; @@ -49,7 +52,28 @@ public function __construct(Connection|string $connOrUrl) if (!class_exists(DriverManager::class)) { throw new InvalidArgumentException(sprintf('Failed to parse the DSN "%s". Try running "composer require doctrine/dbal".', $connOrUrl)); } - $this->conn = DriverManager::getConnection(['url' => $this->filterDsn($connOrUrl)]); + if (class_exists(DsnParser::class)) { + $params = (new DsnParser([ + 'db2' => 'ibm_db2', + 'mssql' => 'pdo_sqlsrv', + 'mysql' => 'pdo_mysql', + 'mysql2' => 'pdo_mysql', + 'postgres' => 'pdo_pgsql', + 'postgresql' => 'pdo_pgsql', + 'pgsql' => 'pdo_pgsql', + 'sqlite' => 'pdo_sqlite', + 'sqlite3' => 'pdo_sqlite', + ]))->parse($this->filterDsn($connOrUrl)); + } else { + $params = ['url' => $this->filterDsn($connOrUrl)]; + } + + $config = new Configuration(); + if (class_exists(DefaultSchemaManagerFactory::class)) { + $config->setSchemaManagerFactory(new DefaultSchemaManagerFactory()); + } + + $this->conn = DriverManager::getConnection($params, $config); } } @@ -246,7 +270,7 @@ private function unlockShared(Key $key): void private function filterDsn(string $dsn): string { if (!str_contains($dsn, '://')) { - throw new InvalidArgumentException(sprintf('String "%" is not a valid DSN for Doctrine DBAL.', $dsn)); + throw new InvalidArgumentException(sprintf('String "%s" is not a valid DSN for Doctrine DBAL.', $dsn)); } [$scheme, $rest] = explode(':', $dsn, 2); diff --git a/src/Symfony/Component/Lock/Store/DoctrineDbalStore.php b/src/Symfony/Component/Lock/Store/DoctrineDbalStore.php index 9ea46a25ff9a2..33c8a471e951c 100644 --- a/src/Symfony/Component/Lock/Store/DoctrineDbalStore.php +++ b/src/Symfony/Component/Lock/Store/DoctrineDbalStore.php @@ -11,12 +11,15 @@ namespace Symfony\Component\Lock\Store; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Connection; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Exception as DBALException; use Doctrine\DBAL\Exception\TableNotFoundException; use Doctrine\DBAL\ParameterType; +use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory; use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Tools\DsnParser; use Symfony\Component\Lock\Exception\InvalidArgumentException; use Symfony\Component\Lock\Exception\InvalidTtlException; use Symfony\Component\Lock\Exception\LockConflictedException; @@ -68,7 +71,28 @@ public function __construct(Connection|string $connOrUrl, array $options = [], f if (!class_exists(DriverManager::class)) { throw new InvalidArgumentException(sprintf('Failed to parse the DSN "%s". Try running "composer require doctrine/dbal".', $connOrUrl)); } - $this->conn = DriverManager::getConnection(['url' => $connOrUrl]); + if (class_exists(DsnParser::class)) { + $params = (new DsnParser([ + 'db2' => 'ibm_db2', + 'mssql' => 'pdo_sqlsrv', + 'mysql' => 'pdo_mysql', + 'mysql2' => 'pdo_mysql', + 'postgres' => 'pdo_pgsql', + 'postgresql' => 'pdo_pgsql', + 'pgsql' => 'pdo_pgsql', + 'sqlite' => 'pdo_sqlite', + 'sqlite3' => 'pdo_sqlite', + ]))->parse($connOrUrl); + } else { + $params = ['url' => $connOrUrl]; + } + + $config = new Configuration(); + if (class_exists(DefaultSchemaManagerFactory::class)) { + $config->setSchemaManagerFactory(new DefaultSchemaManagerFactory()); + } + + $this->conn = DriverManager::getConnection($params, $config); } } diff --git a/src/Symfony/Component/Lock/Tests/Store/DoctrineDbalPostgreSqlStoreTest.php b/src/Symfony/Component/Lock/Tests/Store/DoctrineDbalPostgreSqlStoreTest.php index 132903b92da93..c156d95b807b0 100644 --- a/src/Symfony/Component/Lock/Tests/Store/DoctrineDbalPostgreSqlStoreTest.php +++ b/src/Symfony/Component/Lock/Tests/Store/DoctrineDbalPostgreSqlStoreTest.php @@ -11,9 +11,12 @@ namespace Symfony\Component\Lock\Tests\Store; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Connection; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Exception as DBALException; +use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory; +use Doctrine\DBAL\Tools\DsnParser; use Symfony\Component\Lock\Exception\InvalidArgumentException; use Symfony\Component\Lock\Exception\LockConflictedException; use Symfony\Component\Lock\Key; @@ -38,7 +41,7 @@ public function createPostgreSqlConnection(): Connection $this->markTestSkipped('Missing POSTGRES_HOST env variable'); } - return DriverManager::getConnection(['url' => 'pgsql://postgres:password@'.getenv('POSTGRES_HOST')]); + return self::getDbalConnection('pdo-pgsql://postgres:password@'.getenv('POSTGRES_HOST')); } public function getStore(): PersistingStoreInterface @@ -65,7 +68,7 @@ public function testInvalidDriver($connOrDsn) public static function getInvalidDrivers() { yield ['sqlite:///tmp/foo.db']; - yield [DriverManager::getConnection(['url' => 'sqlite:///tmp/foo.db'])]; + yield [self::getDbalConnection('sqlite:///tmp/foo.db')]; } public function testSaveAfterConflict() @@ -165,4 +168,15 @@ public function testWaitAndSaveReadAfterConflictReleasesLockFromInternalStore() $this->assertTrue($store2->exists($store2Key)); } + + private static function getDbalConnection(string $dsn): Connection + { + $params = class_exists(DsnParser::class) ? (new DsnParser(['sqlite' => 'pdo_sqlite']))->parse($dsn) : ['url' => $dsn]; + $config = new Configuration(); + if (class_exists(DefaultSchemaManagerFactory::class)) { + $config->setSchemaManagerFactory(new DefaultSchemaManagerFactory()); + } + + return DriverManager::getConnection($params, $config); + } } diff --git a/src/Symfony/Component/Lock/Tests/Store/DoctrineDbalStoreTest.php b/src/Symfony/Component/Lock/Tests/Store/DoctrineDbalStoreTest.php index db79dcb3c6952..16c8d3d7465ff 100644 --- a/src/Symfony/Component/Lock/Tests/Store/DoctrineDbalStoreTest.php +++ b/src/Symfony/Component/Lock/Tests/Store/DoctrineDbalStoreTest.php @@ -11,10 +11,12 @@ namespace Symfony\Component\Lock\Tests\Store; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Connection; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Exception\TableNotFoundException; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory; use Symfony\Component\Lock\Key; use Symfony\Component\Lock\PersistingStoreInterface; use Symfony\Component\Lock\Store\DoctrineDbalStore; @@ -36,7 +38,12 @@ public static function setUpBeforeClass(): void { self::$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_lock'); - $store = new DoctrineDbalStore(DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile])); + $config = new Configuration(); + if (class_exists(DefaultSchemaManagerFactory::class)) { + $config->setSchemaManagerFactory(new DefaultSchemaManagerFactory()); + } + + $store = new DoctrineDbalStore(DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile], $config)); $store->createTable(); } @@ -52,7 +59,12 @@ protected function getClockDelay() public function getStore(): PersistingStoreInterface { - return new DoctrineDbalStore(DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile])); + $config = new Configuration(); + if (class_exists(DefaultSchemaManagerFactory::class)) { + $config->setSchemaManagerFactory(new DefaultSchemaManagerFactory()); + } + + return new DoctrineDbalStore(DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile], $config)); } public function testAbortAfterExpiration() diff --git a/src/Symfony/Component/Mailer/Bridge/MailPace/Tests/Transport/MailPaceApiTransportTest.php b/src/Symfony/Component/Mailer/Bridge/MailPace/Tests/Transport/MailPaceApiTransportTest.php index 065c071e52856..c7e99b46d02d4 100644 --- a/src/Symfony/Component/Mailer/Bridge/MailPace/Tests/Transport/MailPaceApiTransportTest.php +++ b/src/Symfony/Component/Mailer/Bridge/MailPace/Tests/Transport/MailPaceApiTransportTest.php @@ -121,6 +121,57 @@ public function testSendThrowsForErrorResponse() $transport->send($mail); } + public function testSendThrowsForErrorsResponse() + { + $client = new MockHttpClient(static function (string $method, string $url, array $options): ResponseInterface { + return new MockResponse(json_encode([ + 'errors' => [ + 'to' => [ + 'contains a blocked address', + 'number of email addresses exceeds maximum volume', + ], + 'attachments.name' => ['Extension file type blocked, see Docs for full list of allowed file types'], + ], + ]), [ + 'http_code' => 400, + 'response_headers' => [ + 'content-type' => 'application/json', + ], + ]); + }); + $transport = new MailPaceApiTransport('KEY', $client); + $transport->setPort(8984); + + $mail = new Email(); + $mail->subject('Hello!') + ->to(new Address('saif.gmati@symfony.com', 'Saif Eddin')) + ->from(new Address('fabpot@symfony.com', 'Fabien')) + ->text('Hello There!'); + + $this->expectException(HttpTransportException::class); + $this->expectExceptionMessage('Unable to send an email: to: contains a blocked address & number of email addresses exceeds maximum volume; attachments.name: Extension file type blocked, see Docs for full list of allowed file types (code 400).'); + $transport->send($mail); + } + + public function testSendThrowsForInternalServerErrorResponse() + { + $client = new MockHttpClient(static function (string $method, string $url, array $options): ResponseInterface { + return new MockResponse('', ['http_code' => 500]); + }); + $transport = new MailPaceApiTransport('KEY', $client); + $transport->setPort(8984); + + $mail = new Email(); + $mail->subject('Hello!') + ->to(new Address('saif.gmati@symfony.com', 'Saif Eddin')) + ->from(new Address('fabpot@symfony.com', 'Fabien')) + ->text('Hello There!'); + + $this->expectException(HttpTransportException::class); + $this->expectExceptionMessage('Unable to send an email: (code 500).'); + $transport->send($mail); + } + public function testTagAndMetadataHeaders() { $email = new Email(); diff --git a/src/Symfony/Component/Mailer/Bridge/MailPace/Transport/MailPaceApiTransport.php b/src/Symfony/Component/Mailer/Bridge/MailPace/Transport/MailPaceApiTransport.php index 8b578612afa8e..9489b41bb7889 100644 --- a/src/Symfony/Component/Mailer/Bridge/MailPace/Transport/MailPaceApiTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/MailPace/Transport/MailPaceApiTransport.php @@ -67,7 +67,20 @@ protected function doSendApi(SentMessage $sentMessage, Email $email, Envelope $e } if (200 !== $statusCode) { - throw new HttpTransportException('Unable to send an email: '.$result['error'].sprintf(' (code %d).', $statusCode), $response); + $errorMessage = 'Unable to send an email: '; + if (isset($result['error'])) { + $errorMessage .= $result['error']; + } elseif (isset($result['errors'])) { + $errors = []; + foreach ($result['errors'] as $key => $val) { + $errors[] = $key.': '.implode(' & ', $val); + } + $errorMessage .= implode('; ', $errors); + } else { + $errorMessage .= 'unknown error'; + } + $errorMessage .= sprintf(' (code %d).', $statusCode); + throw new HttpTransportException($errorMessage, $response); } $sentMessage->setMessageId($result['id']); diff --git a/src/Symfony/Component/Mailer/Transport/Smtp/Stream/SocketStream.php b/src/Symfony/Component/Mailer/Transport/Smtp/Stream/SocketStream.php index 2426730798689..0f7620763f950 100644 --- a/src/Symfony/Component/Mailer/Transport/Smtp/Stream/SocketStream.php +++ b/src/Symfony/Component/Mailer/Transport/Smtp/Stream/SocketStream.php @@ -145,7 +145,7 @@ public function initialize(): void if ($this->streamContextOptions) { $options = array_merge($options, $this->streamContextOptions); } - // do it unconditionnally as it will be used by STARTTLS as well if supported + // do it unconditionally as it will be used by STARTTLS as well if supported $options['ssl']['crypto_method'] ??= \STREAM_CRYPTO_METHOD_TLS_CLIENT | \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT | \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; $streamContext = stream_context_create($options); 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 569f513fa53ab..5af44c4845849 100644 --- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/ConnectionTest.php +++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/ConnectionTest.php @@ -17,6 +17,7 @@ use Doctrine\DBAL\Driver\ResultStatement; use Doctrine\DBAL\Exception as DBALException; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Platforms\MariaDBPlatform; use Doctrine\DBAL\Platforms\MySQL57Platform; use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Platforms\SQLServer2012Platform; @@ -394,6 +395,13 @@ public static function providePlatformSql(): iterable 'SELECT m.* FROM messenger_messages m WHERE (m.delivered_at is null OR m.delivered_at < ?) AND (m.available_at <= ?) AND (m.queue_name = ?) ORDER BY available_at ASC LIMIT 1 FOR UPDATE', ]; + if (class_exists(MariaDBPlatform::class)) { + yield 'MariaDB' => [ + new MariaDBPlatform(), + 'SELECT m.* FROM messenger_messages m WHERE (m.delivered_at is null OR m.delivered_at < ?) AND (m.available_at <= ?) AND (m.queue_name = ?) ORDER BY available_at ASC LIMIT 1 FOR UPDATE', + ]; + } + yield 'SQL Server' => [ new SQLServer2012Platform(), 'SELECT m.* FROM messenger_messages m WITH (UPDLOCK, ROWLOCK) WHERE (m.delivered_at is null OR m.delivered_at < ?) AND (m.available_at <= ?) AND (m.queue_name = ?) ORDER BY available_at ASC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY ', @@ -479,6 +487,13 @@ public function provideFindAllSqlGeneratedByPlatform(): iterable 'SELECT m.* FROM messenger_messages m WHERE (m.delivered_at is null OR m.delivered_at < ?) AND (m.available_at <= ?) AND (m.queue_name = ?) LIMIT 50', ]; + if (class_exists(MariaDBPlatform::class)) { + yield 'MariaDB' => [ + new MariaDBPlatform(), + 'SELECT m.* FROM messenger_messages m WHERE (m.delivered_at is null OR m.delivered_at < ?) AND (m.available_at <= ?) AND (m.queue_name = ?) LIMIT 50', + ]; + } + yield 'SQL Server' => [ new SQLServer2012Platform(), 'SELECT m.* FROM messenger_messages m WHERE (m.delivered_at is null OR m.delivered_at < ?) AND (m.available_at <= ?) AND (m.queue_name = ?) ORDER BY (SELECT 0) OFFSET 0 ROWS FETCH NEXT 50 ROWS ONLY', diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrineIntegrationTest.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrineIntegrationTest.php index 448aa16e6f5b1..e93f6ed56e349 100644 --- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrineIntegrationTest.php +++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrineIntegrationTest.php @@ -11,10 +11,13 @@ namespace Symfony\Component\Messenger\Bridge\Doctrine\Tests\Transport; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Driver\Result as DriverResult; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Result; use Doctrine\DBAL\Schema\AbstractSchemaManager; +use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory; +use Doctrine\DBAL\Tools\DsnParser; use PHPUnit\Framework\TestCase; use Symfony\Component\Messenger\Bridge\Doctrine\Tests\Fixtures\DummyMessage; use Symfony\Component\Messenger\Bridge\Doctrine\Transport\Connection; @@ -31,8 +34,14 @@ class DoctrineIntegrationTest extends TestCase protected function setUp(): void { - $dsn = getenv('MESSENGER_DOCTRINE_DSN') ?: 'sqlite://:memory:'; - $this->driverConnection = DriverManager::getConnection(['url' => $dsn]); + $dsn = getenv('MESSENGER_DOCTRINE_DSN') ?: 'pdo-sqlite://:memory:'; + $params = class_exists(DsnParser::class) ? (new DsnParser())->parse($dsn) : ['url' => $dsn]; + $config = new Configuration(); + if (class_exists(DefaultSchemaManagerFactory::class)) { + $config->setSchemaManagerFactory(new DefaultSchemaManagerFactory()); + } + + $this->driverConnection = DriverManager::getConnection($params, $config); $this->connection = new Connection([], $this->driverConnection); } @@ -57,8 +66,12 @@ public function testSendWithDelay() ->select('m.available_at') ->from('messenger_messages', 'm') ->where('m.body = :body') - ->setParameter('body', '{"message": "Hi i am delayed"}') - ->execute(); + ->setParameter('body', '{"message": "Hi i am delayed"}'); + if (method_exists($stmt, 'executeQuery')) { + $stmt = $stmt->executeQuery(); + } else { + $stmt = $stmt->execute(); + } $available_at = new \DateTimeImmutable($stmt instanceof Result || $stmt instanceof DriverResult ? $stmt->fetchOne() : $stmt->fetchColumn()); @@ -168,7 +181,7 @@ public function testItRetrieveTheMessageThatIsOlderThanRedeliverTimeout() public function testTheTransportIsSetupOnGet() { - $this->assertFalse($this->createSchemaManager()->tablesExist('messenger_messages')); + $this->assertFalse($this->createSchemaManager()->tablesExist(['messenger_messages'])); $this->assertNull($this->connection->get()); $this->connection->send('the body', ['my' => 'header']); diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrinePostgreSqlIntegrationTest.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrinePostgreSqlIntegrationTest.php index 5b4f8a248e30a..63fe3ebd4b513 100644 --- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrinePostgreSqlIntegrationTest.php +++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrinePostgreSqlIntegrationTest.php @@ -11,9 +11,12 @@ namespace Symfony\Component\Messenger\Bridge\Doctrine\Tests\Transport; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Connection; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Schema\AbstractSchemaManager; +use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory; +use Doctrine\DBAL\Tools\DsnParser; use PHPUnit\Framework\TestCase; use Symfony\Component\Messenger\Bridge\Doctrine\Tests\Fixtures\DummyMessage; use Symfony\Component\Messenger\Bridge\Doctrine\Transport\PostgreSqlConnection; @@ -36,7 +39,14 @@ protected function setUp(): void $this->markTestSkipped('Missing POSTGRES_HOST env variable'); } - $this->driverConnection = DriverManager::getConnection(['url' => "pgsql://postgres:password@$host"]); + $url = "pdo-pgsql://postgres:password@$host"; + $params = class_exists(DsnParser::class) ? (new DsnParser())->parse($url) : ['url' => $url]; + $config = new Configuration(); + if (class_exists(DefaultSchemaManagerFactory::class)) { + $config->setSchemaManagerFactory(new DefaultSchemaManagerFactory()); + } + + $this->driverConnection = DriverManager::getConnection($params, $config); $this->connection = new PostgreSqlConnection(['table_name' => 'queue_table'], $this->driverConnection); $this->connection->setup(); } diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php index 78e2cdfbb97d3..e850aa0d41da5 100644 --- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php +++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php @@ -144,9 +144,9 @@ public function send(string $body, array $headers, int $delay = 0): string $now, $availableAt, ], [ - null, - null, - null, + Types::STRING, + Types::STRING, + Types::STRING, Types::DATETIME_MUTABLE, Types::DATETIME_MUTABLE, ]); @@ -273,7 +273,7 @@ public function setup(): void { $configuration = $this->driverConnection->getConfiguration(); $assetFilter = $configuration->getSchemaAssetsFilter(); - $configuration->setSchemaAssetsFilter(null); + $configuration->setSchemaAssetsFilter(static function () { return true; }); $this->updateSchema(); $configuration->setSchemaAssetsFilter($assetFilter); $this->autoSetup = false; @@ -480,13 +480,41 @@ private function updateSchema(): void $schemaManager = $this->createSchemaManager(); $comparator = $this->createComparator($schemaManager); - $schemaDiff = $this->compareSchemas($comparator, $schemaManager->createSchema(), $this->getSchema()); + $schemaDiff = $this->compareSchemas($comparator, method_exists($schemaManager, 'introspectSchema') ? $schemaManager->introspectSchema() : $schemaManager->createSchema(), $this->getSchema()); + $platform = $this->driverConnection->getDatabasePlatform(); + $exec = method_exists($this->driverConnection, 'executeStatement') ? 'executeStatement' : 'exec'; - foreach ($schemaDiff->toSaveSql($this->driverConnection->getDatabasePlatform()) as $sql) { - if (method_exists($this->driverConnection, 'executeStatement')) { - $this->driverConnection->executeStatement($sql); - } else { - $this->driverConnection->exec($sql); + if (!method_exists(SchemaDiff::class, 'getCreatedSchemas')) { + foreach ($schemaDiff->toSaveSql($platform) as $sql) { + $this->driverConnection->$exec($sql); + } + + return; + } + + if ($platform->supportsSchemas()) { + foreach ($schemaDiff->getCreatedSchemas() as $schema) { + $this->driverConnection->$exec($platform->getCreateSchemaSQL($schema)); + } + } + + if ($platform->supportsSequences()) { + foreach ($schemaDiff->getAlteredSequences() as $sequence) { + $this->driverConnection->$exec($platform->getAlterSequenceSQL($sequence)); + } + + foreach ($schemaDiff->getCreatedSequences() as $sequence) { + $this->driverConnection->$exec($platform->getCreateSequenceSQL($sequence)); + } + } + + foreach ($platform->getCreateTablesSQL($schemaDiff->getCreatedTables()) as $sql) { + $this->driverConnection->$exec($sql); + } + + foreach ($schemaDiff->getAlteredTables() as $tableDiff) { + foreach ($platform->getAlterTableSQL($tableDiff) as $sql) { + $this->driverConnection->$exec($sql); } } } @@ -507,7 +535,7 @@ private function createComparator(AbstractSchemaManager $schemaManager): Compara private function compareSchemas(Comparator $comparator, Schema $from, Schema $to): SchemaDiff { - return method_exists($comparator, 'compareSchemas') + return method_exists($comparator, 'compareSchemas') || method_exists($comparator, 'doCompareSchemas') ? $comparator->compareSchemas($from, $to) : $comparator->compare($from, $to); } diff --git a/src/Symfony/Component/Messenger/EventListener/SendFailedMessageForRetryListener.php b/src/Symfony/Component/Messenger/EventListener/SendFailedMessageForRetryListener.php index 0af2673d1e13d..0f071e82e702d 100644 --- a/src/Symfony/Component/Messenger/EventListener/SendFailedMessageForRetryListener.php +++ b/src/Symfony/Component/Messenger/EventListener/SendFailedMessageForRetryListener.php @@ -117,8 +117,7 @@ public static function getSubscribedEvents(): array private function shouldRetry(\Throwable $e, Envelope $envelope, RetryStrategyInterface $retryStrategy): bool { - $isRetryable = $retryStrategy->isRetryable($envelope, $e); - if ($isRetryable && $e instanceof RecoverableExceptionInterface) { + if ($e instanceof RecoverableExceptionInterface) { return true; } @@ -127,7 +126,7 @@ private function shouldRetry(\Throwable $e, Envelope $envelope, RetryStrategyInt if ($e instanceof HandlerFailedException) { $shouldNotRetry = true; foreach ($e->getNestedExceptions() as $nestedException) { - if ($isRetryable && $nestedException instanceof RecoverableExceptionInterface) { + if ($nestedException instanceof RecoverableExceptionInterface) { return true; } @@ -145,7 +144,7 @@ private function shouldRetry(\Throwable $e, Envelope $envelope, RetryStrategyInt return false; } - return $isRetryable; + return $retryStrategy->isRetryable($envelope, $e); } private function getRetryStrategyForTransport(string $alias): ?RetryStrategyInterface diff --git a/src/Symfony/Component/Messenger/Tests/EventListener/SendFailedMessageForRetryListenerTest.php b/src/Symfony/Component/Messenger/Tests/EventListener/SendFailedMessageForRetryListenerTest.php index cb79f539dc325..fccdae9bfa215 100644 --- a/src/Symfony/Component/Messenger/Tests/EventListener/SendFailedMessageForRetryListenerTest.php +++ b/src/Symfony/Component/Messenger/Tests/EventListener/SendFailedMessageForRetryListenerTest.php @@ -63,7 +63,7 @@ public function testRecoverableStrategyCausesRetry() $senderLocator->expects($this->once())->method('has')->willReturn(true); $senderLocator->expects($this->once())->method('get')->willReturn($sender); $retryStategy = $this->createMock(RetryStrategyInterface::class); - $retryStategy->expects($this->once())->method('isRetryable')->willReturn(true); + $retryStategy->expects($this->never())->method('isRetryable'); $retryStategy->expects($this->once())->method('getWaitingTime')->willReturn(1000); $retryStrategyLocator = $this->createMock(ContainerInterface::class); $retryStrategyLocator->expects($this->once())->method('has')->willReturn(true); @@ -78,27 +78,6 @@ public function testRecoverableStrategyCausesRetry() $listener->onMessageFailed($event); } - public function testRetryIsOnlyAllowedWhenPermittedByRetryStrategy() - { - $senderLocator = $this->createMock(ContainerInterface::class); - $senderLocator->expects($this->never())->method('has'); - $senderLocator->expects($this->never())->method('get'); - $retryStrategy = $this->createMock(RetryStrategyInterface::class); - $retryStrategy->expects($this->once())->method('isRetryable')->willReturn(false); - $retryStrategy->expects($this->never())->method('getWaitingTime'); - $retryStrategyLocator = $this->createMock(ContainerInterface::class); - $retryStrategyLocator->expects($this->once())->method('has')->willReturn(true); - $retryStrategyLocator->expects($this->once())->method('get')->willReturn($retryStrategy); - - $listener = new SendFailedMessageForRetryListener($senderLocator, $retryStrategyLocator); - - $exception = new RecoverableMessageHandlingException('retry'); - $envelope = new Envelope(new \stdClass()); - $event = new WorkerMessageFailedEvent($envelope, 'my_receiver', $exception); - - $listener->onMessageFailed($event); - } - public function testEnvelopeIsSentToTransportOnRetry() { $exception = new \Exception('no!'); diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/CompiledUrlMatcherDumper.php b/src/Symfony/Component/Routing/Matcher/Dumper/CompiledUrlMatcherDumper.php index 980aa8e51f864..b0f8418971deb 100644 --- a/src/Symfony/Component/Routing/Matcher/Dumper/CompiledUrlMatcherDumper.php +++ b/src/Symfony/Component/Routing/Matcher/Dumper/CompiledUrlMatcherDumper.php @@ -136,7 +136,8 @@ private function generateCompiledRoutes(): string foreach ($staticRoutes as $path => $routes) { $code .= sprintf(" %s => [\n", self::export($path)); foreach ($routes as $route) { - $code .= sprintf(" [%s, %s, %s, %s, %s, %s, %s],\n", ...array_map([__CLASS__, 'export'], $route)); + $r = array_map([__CLASS__, 'export'], $route); + $code .= sprintf(" [%s, %s, %s, %s, %s, %s, %s],\n", $r[0], $r[1], $r[2], $r[3], $r[4], $r[5], $r[6]); } $code .= " ],\n"; } @@ -148,7 +149,8 @@ private function generateCompiledRoutes(): string foreach ($dynamicRoutes as $path => $routes) { $code .= sprintf(" %s => [\n", self::export($path)); foreach ($routes as $route) { - $code .= sprintf(" [%s, %s, %s, %s, %s, %s, %s],\n", ...array_map([__CLASS__, 'export'], $route)); + $r = array_map([__CLASS__, 'export'], $route); + $code .= sprintf(" [%s, %s, %s, %s, %s, %s, %s],\n", $r[0], $r[1], $r[2], $r[3], $r[4], $r[5], $r[6]); } $code .= " ],\n"; } diff --git a/src/Symfony/Component/Runtime/Internal/ComposerPlugin.php b/src/Symfony/Component/Runtime/Internal/ComposerPlugin.php index a48ff46ff00b1..1861676ad30ab 100644 --- a/src/Symfony/Component/Runtime/Internal/ComposerPlugin.php +++ b/src/Symfony/Component/Runtime/Internal/ComposerPlugin.php @@ -105,7 +105,12 @@ public function updateAutoloadFile(): void '%runtime_options%' => '['.substr(var_export($extra, true), 7, -1)." 'project_dir' => {$projectDir},\n]", ]); - file_put_contents(substr_replace($autoloadFile, '_runtime', -4, 0), $code); + // could use Composer\Util\Filesystem::filePutContentsIfModified once Composer 1.x support is dropped for this plugin + $path = substr_replace($autoloadFile, '_runtime', -4, 0); + $currentContent = @file_exists($path) ? @file_get_contents($path) : false; + if (false === $currentContent || $currentContent !== $code) { + file_put_contents($path, $code); + } } public static function getSubscribedEvents(): array diff --git a/src/Symfony/Component/Runtime/composer.json b/src/Symfony/Component/Runtime/composer.json index a9f91def34935..7d0ac18a282fa 100644 --- a/src/Symfony/Component/Runtime/composer.json +++ b/src/Symfony/Component/Runtime/composer.json @@ -21,7 +21,7 @@ }, "require-dev": { "composer/composer": "^1.0.2|^2.0", - "symfony/console": "^5.4|^6.0", + "symfony/console": "^5.4.9|^6.0.9", "symfony/dotenv": "^5.4|^6.0", "symfony/http-foundation": "^5.4|^6.0", "symfony/http-kernel": "^5.4|^6.0" diff --git a/src/Symfony/Component/Serializer/Context/Encoder/YamlEncoderContextBuilder.php b/src/Symfony/Component/Serializer/Context/Encoder/YamlEncoderContextBuilder.php index 8bff9de933413..63efb71d7005b 100644 --- a/src/Symfony/Component/Serializer/Context/Encoder/YamlEncoderContextBuilder.php +++ b/src/Symfony/Component/Serializer/Context/Encoder/YamlEncoderContextBuilder.php @@ -59,7 +59,7 @@ public function withFlags(?int $flags): static } /** - * Configures whether to perserve empty objects "{}" or to convert them to null. + * Configures whether to preserve empty objects "{}" or to convert them to null. */ public function withPreservedEmptyObjects(?bool $preserveEmptyObjects): static { diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 69dc534e417fb..98ccfca4e4702 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -780,6 +780,10 @@ private function removeNestedValue(array $path, array $data): array */ private function getMappedClass(array $data, string $class, array $context): string { + if (null !== $object = $this->extractObjectToPopulate($class, $context, self::OBJECT_TO_POPULATE)) { + return $object::class; + } + if (!$mapping = $this->classDiscriminatorResolver?->getMappingForClass($class)) { return $class; } diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index d876193c8a741..53ef55bd1bf70 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -34,6 +34,7 @@ use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter; +use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; @@ -44,6 +45,7 @@ use Symfony\Component\Serializer\Tests\Fixtures\Annotations\AbstractDummy; use Symfony\Component\Serializer\Tests\Fixtures\Annotations\AbstractDummyFirstChild; use Symfony\Component\Serializer\Tests\Fixtures\Annotations\AbstractDummySecondChild; +use Symfony\Component\Serializer\Tests\Fixtures\DummyFirstChildQuux; use Symfony\Component\Serializer\Tests\Fixtures\DummySecondChildQuux; use Symfony\Component\Serializer\Tests\Normalizer\Features\ObjectDummyWithContextAttribute; @@ -480,6 +482,61 @@ public function hasMetadataFor($value): bool $this->assertInstanceOf(DummySecondChildQuux::class, $normalizedData->quux); } + public function testDenormalizeWithDiscriminatorMapAndObjectToPopulateUsesCorrectClassname() + { + $factory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); + + $loaderMock = new class() implements ClassMetadataFactoryInterface { + public function getMetadataFor($value): ClassMetadataInterface + { + if (AbstractDummy::class === $value) { + return new ClassMetadata( + AbstractDummy::class, + new ClassDiscriminatorMapping('type', [ + 'first' => AbstractDummyFirstChild::class, + 'second' => AbstractDummySecondChild::class, + ]) + ); + } + + throw new InvalidArgumentException(); + } + + public function hasMetadataFor($value): bool + { + return AbstractDummy::class === $value; + } + }; + + $discriminatorResolver = new ClassDiscriminatorFromClassMetadata($loaderMock); + $normalizer = new AbstractObjectNormalizerDummy($factory, null, new PhpDocExtractor(), $discriminatorResolver); + $serializer = new Serializer([$normalizer]); + $normalizer->setSerializer($serializer); + + $data = [ + 'foo' => 'foo', + 'quux' => ['value' => 'quux'], + ]; + + $normalizedData1 = $normalizer->denormalize($data + ['bar' => 'bar'], AbstractDummy::class, 'any', [ + AbstractNormalizer::OBJECT_TO_POPULATE => new AbstractDummyFirstChild('notfoo', 'notbar'), + ]); + $this->assertInstanceOf(AbstractDummyFirstChild::class, $normalizedData1); + $this->assertSame('foo', $normalizedData1->foo); + $this->assertSame('notbar', $normalizedData1->bar); + $this->assertInstanceOf(DummyFirstChildQuux::class, $normalizedData1->quux); + $this->assertSame('quux', $normalizedData1->quux->getValue()); + + $normalizedData2 = $normalizer->denormalize($data + ['baz' => 'baz'], AbstractDummy::class, 'any', [ + AbstractNormalizer::OBJECT_TO_POPULATE => new AbstractDummySecondChild('notfoo', 'notbaz'), + ]); + $this->assertInstanceOf(AbstractDummySecondChild::class, $normalizedData2); + $this->assertSame('foo', $normalizedData2->foo); + $this->assertSame('baz', $normalizedData2->baz); + $this->assertInstanceOf(DummySecondChildQuux::class, $normalizedData2->quux); + $this->assertSame('quux', $normalizedData2->quux->getValue()); + } + public function testDenormalizeWithNestedDiscriminatorMap() { $classDiscriminatorResolver = new class() implements ClassDiscriminatorResolverInterface { diff --git a/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php b/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php index c81eef8ab4659..b3a2eee4da9b8 100644 --- a/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php +++ b/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php @@ -117,96 +117,75 @@ public function getValidator(): ValidatorInterface; public function getObject(): ?object; /** - * Sets the currently validated value. + * Warning: Should not be called by user code, to be used by the validator engine only. * * @param object|null $object The currently validated object * @param string $propertyPath The property path to the current value - * - * @internal Used by the validator engine. Should not be called by user - * code. */ public function setNode(mixed $value, ?object $object, MetadataInterface $metadata = null, string $propertyPath); /** - * Sets the currently validated group. + * Warning: Should not be called by user code, to be used by the validator engine only. * * @param string|null $group The validated group - * - * @internal Used by the validator engine. Should not be called by user - * code. */ public function setGroup(?string $group); /** - * Sets the currently validated constraint. - * - * @internal Used by the validator engine. Should not be called by user - * code. + * Warning: Should not be called by user code, to be used by the validator engine only. */ public function setConstraint(Constraint $constraint); /** - * Marks an object as validated in a specific validation group. + * Warning: Should not be called by user code, to be used by the validator engine only. * * @param string $cacheKey The hash of the object * @param string $groupHash The group's name or hash, if it is group * sequence - * - * @internal Used by the validator engine. Should not be called by user - * code. */ public function markGroupAsValidated(string $cacheKey, string $groupHash); /** - * Returns whether an object was validated in a specific validation group. + * Warning: Should not be called by user code, to be used by the validator engine only. * * @param string $cacheKey The hash of the object * @param string $groupHash The group's name or hash, if it is group * sequence * - * @internal Used by the validator engine */ public function isGroupValidated(string $cacheKey, string $groupHash): bool; /** - * Marks a constraint as validated for an object. + * Warning: Should not be called by user code, to be used by the validator engine only. * * @param string $cacheKey The hash of the object * @param string $constraintHash The hash of the constraint - * - * @internal Used by the validator engine. Should not be called by user - * code. */ public function markConstraintAsValidated(string $cacheKey, string $constraintHash); /** - * Returns whether a constraint was validated for an object. + * Warning: Should not be called by user code, to be used by the validator engine only. * * @param string $cacheKey The hash of the object * @param string $constraintHash The hash of the constraint * - * @internal Used by the validator engine */ public function isConstraintValidated(string $cacheKey, string $constraintHash): bool; /** - * Marks that an object was initialized. + * Warning: Should not be called by user code, to be used by the validator engine only. * * @param string $cacheKey The hash of the object * - * @internal Used by the validator engine. Should not be called by user - * code. - * * @see ObjectInitializerInterface */ public function markObjectAsInitialized(string $cacheKey); /** - * Returns whether an object was initialized. + * Warning: Should not be called by user code, to be used by the validator engine only. * * @param string $cacheKey The hash of the object * - * @internal Used by the validator engine * * @see ObjectInitializerInterface */ diff --git a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php index 5d958985d312c..87a90798f165d 100644 --- a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php +++ b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php @@ -340,16 +340,17 @@ public function mergeConstraints(self $source) $constraint->addImplicitGroupName($this->getDefaultGroup()); } - $this->addPropertyMetadata($member); - if ($member instanceof MemberMetadata && !$member->isPrivate($this->name)) { $property = $member->getPropertyName(); + $this->members[$property] = [$member]; - if ($member instanceof PropertyMetadata && !isset($this->properties[$property])) { + if ($member instanceof PropertyMetadata) { $this->properties[$property] = $member; - } elseif ($member instanceof GetterMetadata && !isset($this->getters[$property])) { + } elseif ($member instanceof GetterMetadata) { $this->getters[$property] = $member; } + } else { + $this->addPropertyMetadata($member); } } } diff --git a/src/Symfony/Component/Validator/Mapping/Loader/AnnotationLoader.php b/src/Symfony/Component/Validator/Mapping/Loader/AnnotationLoader.php index a2842343f2be0..b0252afc3d208 100644 --- a/src/Symfony/Component/Validator/Mapping/Loader/AnnotationLoader.php +++ b/src/Symfony/Component/Validator/Mapping/Loader/AnnotationLoader.php @@ -92,27 +92,49 @@ public function loadClassMetadata(ClassMetadata $metadata): bool */ private function getAnnotations(object $reflection): iterable { + $dedup = []; + foreach ($reflection->getAttributes(GroupSequence::class) as $attribute) { + $dedup[] = $attribute->newInstance(); yield $attribute->newInstance(); } foreach ($reflection->getAttributes(GroupSequenceProvider::class) as $attribute) { + $dedup[] = $attribute->newInstance(); yield $attribute->newInstance(); } foreach ($reflection->getAttributes(Constraint::class, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) { + $dedup[] = $attribute->newInstance(); yield $attribute->newInstance(); } if (!$this->reader) { return; } + $annotations = []; + if ($reflection instanceof \ReflectionClass) { - yield from $this->reader->getClassAnnotations($reflection); + $annotations = $this->reader->getClassAnnotations($reflection); } if ($reflection instanceof \ReflectionMethod) { - yield from $this->reader->getMethodAnnotations($reflection); + $annotations = $this->reader->getMethodAnnotations($reflection); } if ($reflection instanceof \ReflectionProperty) { - yield from $this->reader->getPropertyAnnotations($reflection); + $annotations = $this->reader->getPropertyAnnotations($reflection); + } + + foreach ($dedup as $annotation) { + if ($annotation instanceof Constraint) { + $annotation->groups; // trigger initialization of the "groups" property + } + } + + foreach ($annotations as $annotation) { + if ($annotation instanceof Constraint) { + $annotation->groups; // trigger initialization of the "groups" property + } + if (!\in_array($annotation, $dedup, false)) { + yield $annotation; + } } } } diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf index f6772fdfb67ba..aaf6ada6fc089 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf @@ -406,6 +406,26 @@ The filename is too long. It should have {{ filename_max_length }} character or less.|The filename is too long. It should have {{ filename_max_length }} characters or less. The filename is too long. It should have {{ filename_max_length }} character or less.|The filename is too long. It should have {{ filename_max_length }} characters or less. + + The password strength is too low. Please use a stronger password. + The password strength is too low. Please use a stronger password. + + + This value contains characters that are not allowed by the current restriction-level. + This value contains characters that are not allowed by the current restriction-level. + + + Using invisible characters is not allowed. + Using invisible characters is not allowed. + + + Mixing numbers from different scripts is not allowed. + Mixing numbers from different scripts is not allowed. + + + Using hidden overlay characters is not allowed. + Using hidden overlay characters is not allowed. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf index 92127773178e7..a1186891f4ad2 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf @@ -402,6 +402,30 @@ The value of the netmask should be between {{ min }} and {{ max }}. La valeur du masque de réseau doit être comprise entre {{ min }} et {{ max }}. + + The filename is too long. It should have {{ filename_max_length }} character or less.|The filename is too long. It should have {{ filename_max_length }} characters or less. + Le nom du fichier est trop long. Il doit contenir au maximum {{ filename_max_length }} caractère.|Le nom de fichier est trop long. Il doit contenir au maximum {{ filename_max_length }} caractères. + + + The password strength is too low. Please use a stronger password. + La force du mot de passe est trop faible. Veuillez utiliser un mot de passe plus fort. + + + This value contains characters that are not allowed by the current restriction-level. + Cette valeur contient des caractères qui ne sont pas autorisés par le niveau de restriction actuel. + + + Using invisible characters is not allowed. + Utiliser des caractères invisibles n'est pas autorisé. + + + Mixing numbers from different scripts is not allowed. + Mélanger des chiffres provenant de différents scripts n'est pas autorisé. + + + Using hidden overlay characters is not allowed. + Utiliser des caractères de superposition cachés n'est pas autorisé. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.pl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.pl.xlf index b983b2d6c877f..e20f490970958 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.pl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.pl.xlf @@ -402,6 +402,30 @@ The value of the netmask should be between {{ min }} and {{ max }}. Wartość maski podsieci powinna być pomiędzy {{ min }} i {{ max }}. + + The filename is too long. It should have {{ filename_max_length }} character or less.|The filename is too long. It should have {{ filename_max_length }} characters or less. + Nazwa pliku jest za długa. Powinna mieć {{ filename_max_length }} znak lub mniej.|Nazwa pliku jest za długa. Powinna mieć {{ filename_max_length }} znaków lub mniej. + + + The password strength is too low. Please use a stronger password. + Siła hasła jest zbyt niska. Użyj mocniejszego hasła. + + + This value contains characters that are not allowed by the current restriction-level. + Ta wartość zawiera znaki, które nie są dozwolone przez aktualny poziom ograniczeń. + + + Using invisible characters is not allowed. + Używanie niewidzialnych znaków jest niedozwolone. + + + Mixing numbers from different scripts is not allowed. + Mieszanie liczb z różnych skryptów jest niedozwolone. + + + Using hidden overlay characters is not allowed. + Używanie ukrytych znaków nakładki jest niedozwolone. + diff --git a/src/Symfony/Component/VarDumper/Caster/DateCaster.php b/src/Symfony/Component/VarDumper/Caster/DateCaster.php index 07e8c5ea8cf55..b67cf73f1ec43 100644 --- a/src/Symfony/Component/VarDumper/Caster/DateCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/DateCaster.php @@ -27,7 +27,7 @@ class DateCaster public static function castDateTime(\DateTimeInterface $d, array $a, Stub $stub, bool $isNested, int $filter) { $prefix = Caster::PREFIX_VIRTUAL; - $location = $d->getTimezone()->getLocation(); + $location = $d->getTimezone() ? $d->getTimezone()->getLocation() : null; $fromNow = (new \DateTimeImmutable())->diff($d); $title = $d->format('l, F j, Y') diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php index fbbd901fa74b4..ef58b0ce74f8d 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php @@ -106,6 +106,52 @@ public static function provideDateTimes() ]; } + /** + * @dataProvider provideNoTimezoneDateTimes + */ + public function testCastDateTimeNoTimezone($time, $xDate, $xInfos) + { + $stub = new Stub(); + $date = new NoTimezoneDate($time); + $cast = DateCaster::castDateTime($date, Caster::castObject($date, \DateTime::class), $stub, false, 0); + + $xDump = << $xDate +] +EODUMP; + + $this->assertDumpEquals($xDump, $cast); + + $xDump = <<assertDumpMatchesFormat($xDump, $cast["\0~\0date"]); + } + + public static function provideNoTimezoneDateTimes() + { + return [ + ['2017-04-30 00:00:00.000000', '2017-04-30 00:00:00.0 +00:00', 'Sunday, April 30, 2017'], + ['2017-04-30 00:00:00.100000', '2017-04-30 00:00:00.100 +00:00', 'Sunday, April 30, 2017'], + ['2017-04-30 00:00:00.120000', '2017-04-30 00:00:00.120 +00:00', 'Sunday, April 30, 2017'], + ['2017-04-30 00:00:00.123000', '2017-04-30 00:00:00.123 +00:00', 'Sunday, April 30, 2017'], + ['2017-04-30 00:00:00.123400', '2017-04-30 00:00:00.123400 +00:00', 'Sunday, April 30, 2017'], + ['2017-04-30 00:00:00.123450', '2017-04-30 00:00:00.123450 +00:00', 'Sunday, April 30, 2017'], + ['2017-04-30 00:00:00.123456', '2017-04-30 00:00:00.123456 +00:00', 'Sunday, April 30, 2017'], + ]; + } + public function testCastDateTimeWithAdditionalChildProperty() { $stub = new Stub(); @@ -423,3 +469,11 @@ private function createInterval($intervalSpec, $ms, $invert) return $interval; } } + +class NoTimezoneDate extends \DateTime +{ + public function getTimezone(): \DateTimeZone|false + { + return false; + } +} diff --git a/src/Symfony/Contracts/Translation/Test/TranslatorTest.php b/src/Symfony/Contracts/Translation/Test/TranslatorTest.php index e4168cc14bf09..674b78b3eae5b 100644 --- a/src/Symfony/Contracts/Translation/Test/TranslatorTest.php +++ b/src/Symfony/Contracts/Translation/Test/TranslatorTest.php @@ -255,13 +255,13 @@ public static function getChooseTests() new-line in it. Selector = 0.|{1}This is a text with a new-line in it. Selector = 1.|[1,Inf]This is a text with a new-line in it. Selector > 1.', 5], - // with double-quotes and id split accros lines + // with double-quotes and id split across lines ['This is a text with a new-line in it. Selector = 1.', '{0}This is a text with a new-line in it. Selector = 0.|{1}This is a text with a new-line in it. Selector = 1.|[1,Inf]This is a text with a new-line in it. Selector > 1.', 1], - // with single-quotes and id split accros lines + // with single-quotes and id split across lines ['This is a text with a new-line in it. Selector > 1.', '{0}This is a text with a new-line in it. Selector = 0.|{1}This is a text with a @@ -269,7 +269,7 @@ public static function getChooseTests() new-line in it. Selector > 1.', 5], // with single-quotes and \n in text ['This is a text with a\nnew-line in it. Selector = 0.', '{0}This is a text with a\nnew-line in it. Selector = 0.|{1}This is a text with a\nnew-line in it. Selector = 1.|[1,Inf]This is a text with a\nnew-line in it. Selector > 1.', 0], - // with double-quotes and id split accros lines + // with double-quotes and id split across lines ["This is a text with a\nnew-line in it. Selector = 1.", "{0}This is a text with a\nnew-line in it. Selector = 0.|{1}This is a text with a\nnew-line in it. Selector = 1.|[1,Inf]This is a text with a\nnew-line in it. Selector > 1.", 1], // escape pipe ['This is a text with | in it. Selector = 0.', '{0}This is a text with || in it. Selector = 0.|{1}This is a text with || in it. Selector = 1.', 0],