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], pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy