diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index f749de5e0d82a..c2e5d98e69343 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,6 @@ | Q | A | ------------- | --- -| Branch? | 7.2 for features / 5.4, 6.4, 7.0, and 7.1 for bug fixes +| Branch? | 7.2 for features / 5.4, 6.4, and 7.1 for bug fixes | Bug fix? | yes/no | New feature? | yes/no | Deprecations? | yes/no diff --git a/.github/expected-missing-return-types.diff b/.github/expected-missing-return-types.diff index d5326aec3d5e5..da6971fe1d361 100644 --- a/.github/expected-missing-return-types.diff +++ b/.github/expected-missing-return-types.diff @@ -1,5 +1,5 @@ # Run these steps to update this file: -sed -i 's/ *"\*\*\/Tests\/"//' composer.json +sed -i 's/ *"\*\*\/Tests\/",//' composer.json composer u -o SYMFONY_PATCH_TYPE_DECLARATIONS='force=2&php=8.1' php .github/patch-types.php head=$(sed '/^diff /Q' .github/expected-missing-return-types.diff) @@ -7063,7 +7063,7 @@ diff --git a/src/Symfony/Component/HttpClient/DecoratorTrait.php b/src/Symfony/C diff --git a/src/Symfony/Component/HttpClient/HttpClientTrait.php b/src/Symfony/Component/HttpClient/HttpClientTrait.php --- a/src/Symfony/Component/HttpClient/HttpClientTrait.php +++ b/src/Symfony/Component/HttpClient/HttpClientTrait.php -@@ -679,5 +679,5 @@ trait HttpClientTrait +@@ -685,5 +685,5 @@ trait HttpClientTrait * @return string */ - private static function removeDotSegments(string $path) @@ -8457,28 +8457,28 @@ diff --git a/src/Symfony/Component/HttpKernel/HttpCache/Esi.php b/src/Symfony/Co diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php --- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php -@@ -246,5 +246,5 @@ class HttpCache implements HttpKernelInterface, TerminableInterface +@@ -248,5 +248,5 @@ class HttpCache implements HttpKernelInterface, TerminableInterface * @return void */ - public function terminate(Request $request, Response $response) + public function terminate(Request $request, Response $response): void { // Do not call any listeners in case of a cache hit. -@@ -466,5 +466,5 @@ class HttpCache implements HttpKernelInterface, TerminableInterface +@@ -468,5 +468,5 @@ class HttpCache implements HttpKernelInterface, TerminableInterface * @return Response */ - protected function forward(Request $request, bool $catch = false, ?Response $entry = null) + protected function forward(Request $request, bool $catch = false, ?Response $entry = null): Response { $this->surrogate?->addSurrogateCapability($request); -@@ -600,5 +600,5 @@ class HttpCache implements HttpKernelInterface, TerminableInterface +@@ -602,5 +602,5 @@ class HttpCache implements HttpKernelInterface, TerminableInterface * @throws \Exception */ - protected function store(Request $request, Response $response) + protected function store(Request $request, Response $response): void { try { -@@ -678,5 +678,5 @@ class HttpCache implements HttpKernelInterface, TerminableInterface +@@ -680,5 +680,5 @@ class HttpCache implements HttpKernelInterface, TerminableInterface * @return void */ - protected function processResponseBody(Request $request, Response $response) @@ -9943,7 +9943,7 @@ diff --git a/src/Symfony/Component/Mime/Part/TextPart.php b/src/Symfony/Componen diff --git a/src/Symfony/Component/Mime/RawMessage.php b/src/Symfony/Component/Mime/RawMessage.php --- a/src/Symfony/Component/Mime/RawMessage.php +++ b/src/Symfony/Component/Mime/RawMessage.php -@@ -97,5 +97,5 @@ class RawMessage +@@ -100,5 +100,5 @@ class RawMessage * @throws LogicException if the message is not valid */ - public function ensureValidity() @@ -10147,7 +10147,7 @@ diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Sym - public function setValue(object|array &$objectOrArray, string|PropertyPathInterface $propertyPath, mixed $value) + public function setValue(object|array &$objectOrArray, string|PropertyPathInterface $propertyPath, mixed $value): void { - if (\is_object($objectOrArray) && false === strpbrk((string) $propertyPath, '.[')) { + if (\is_object($objectOrArray) && (false === strpbrk((string) $propertyPath, '.[') || $objectOrArray instanceof \stdClass && property_exists($objectOrArray, $propertyPath))) { diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessorInterface.php b/src/Symfony/Component/PropertyAccess/PropertyAccessorInterface.php --- a/src/Symfony/Component/PropertyAccess/PropertyAccessorInterface.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessorInterface.php @@ -12808,7 +12808,7 @@ diff --git a/src/Symfony/Component/Validator/Constraints/HostnameValidator.php b diff --git a/src/Symfony/Component/Validator/Constraints/IbanValidator.php b/src/Symfony/Component/Validator/Constraints/IbanValidator.php --- a/src/Symfony/Component/Validator/Constraints/IbanValidator.php +++ b/src/Symfony/Component/Validator/Constraints/IbanValidator.php -@@ -167,5 +167,5 @@ class IbanValidator extends ConstraintValidator +@@ -171,5 +171,5 @@ class IbanValidator extends ConstraintValidator * @return void */ - public function validate(mixed $value, Constraint $constraint) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index a5bbb43d58e30..60d2cdaf5a2e1 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -44,6 +44,13 @@ jobs: LDAP_PORT_NUMBER: 3389 LDAP_USERS: a LDAP_PASSWORDS: a + ftp: + image: onekilo79/ftpd_test + ports: + - 21:21 + - 30000-30009:30000-30009 + volumes: + - ./:/hostmount redis: image: redis:6.2.8 ports: @@ -162,6 +169,11 @@ jobs: curl -s -u Administrator:111111 -X POST http://localhost:8091/pools/default/buckets -d 'ramQuotaMB=100&bucketType=ephemeral&name=cache' curl -s -u Administrator:111111 -X POST http://localhost:8091/pools/default -d 'memoryQuota=256' + - name: Create FTP fixtures + run: | + mkdir -p ./ftpusers/test/pub + touch ./ftpusers/test/pub/example ./ftpusers/test/readme.txt + - name: Setup PHP uses: shivammathur/setup-php@v2 with: @@ -212,6 +224,7 @@ jobs: - name: Run tests run: ./phpunit --group integration -v env: + INTEGRATION_FTP_URL: 'ftp://test:test@localhost' REDIS_HOST: 'localhost:16379' REDIS_AUTHENTICATED_HOST: 'localhost:16380' REDIS_CLUSTER_HOSTS: 'localhost:7000 localhost:7001 localhost:7002 localhost:7003 localhost:7004 localhost:7005' diff --git a/CHANGELOG-6.4.md b/CHANGELOG-6.4.md index 8059eaa560721..56efbaa4a9bbf 100644 --- a/CHANGELOG-6.4.md +++ b/CHANGELOG-6.4.md @@ -7,6 +7,44 @@ in 6.4 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v6.4.0...v6.4.1 +* 6.4.11 (2024-08-30) + + * bug #58110 [PropertyAccess] Fix handling property names with a `.` (alexandre-daubois) + * bug #58127 [Validator] synchronize IBAN formats (xabbuh) + * bug #58112 fix Twig 3.12 compatibility (xabbuh) + * bug #58078 [TwigBridge] Fix Twig deprecation notice (yceruto) + * bug #58000 [DependencyInjection] Fix issue between decorator and service locator index (lyrixx) + * bug #58044 [HttpClient] Do not overwrite the host to request when using option "resolve" (xabbuh) + * bug #58046 [AssetMapper] Fix JsDeliver import regexp (smnandre) + * bug #57298 [DependencyInjection] Fix handling of repeated `#[Autoconfigure]` attributes (alexandre-daubois) + * bug #57493 [SecurityBundle] Make security schema deterministic (MatTheCat) + * bug #58025 [Mailer] [Brevo] Support the `unique_proxy_open` event (richardhj) + * bug #58015 [HttpKernel] ESI fragment content may be missing in conditional requests (mpdude) + * bug #58017 [SecurityBundle] Revert adding `_stateless` attribute to the request when firewall is stateless and the attribute is not already set (MatTheCat) + * bug #58020 [TwigBridge] fix compatibility with Twig 3.12 and 4.0 (xabbuh) + * bug #58002 [Security] Revert stateless check for ContextListener (VincentLanglet) + * bug #58010 [PsrHttpMessageBridge] Fix conversion of partitioned cookies in the PSR-7 bridge (stof) + * bug #57853 [Console] Fix side-effects from running bash completions (Seldaek) + * bug #57997 [Console][PhpUnitBridge][VarDumper] Fix handling NO_COLOR env var (nicolas-grekas) + * bug #57944 [DoctrineBridge] Fix the `LockStoreSchemaListener` (MatTheCat) + * bug #57984 [Validator] Add `D` regex modifier in relevant validators (alexandre-daubois) + * bug #57981 [HttpClient] reject malformed URLs with a meaningful exception (xabbuh) + * bug #57968 [Yaml] :bug: throw ParseException on invalid date (homersimpsons) + * bug #57925 [Validator] reset the validation context after validating nested constraints (xabbuh) + * bug #57920 [Form] Fix handling empty data in ValueToDuplicatesTransformer (xabbuh) + * bug #57917 [HttpKernel] [WebProfileBundle] Fix Routing panel for URLs with a colon (akeylimepie) + * bug #57885 [Cache] fix compatibility with redis extension 6.0.3+ (xabbuh) + * bug #57861 [Form] NumberType: Fix parsing of numbers in exponential notation with negative exponent (jbtronics) + * bug #57921 [Finder] do not duplicate directory separators (xabbuh) + * bug #57875 [String] Fixed Quorum plural, and Quora singular in EnglishInflector (Dean151) + * bug #57895 [Finder] do not duplicate directory separators (xabbuh) + * bug #57905 [Validator] allow more unicode characters in URL paths (xabbuh) + * bug #57899 [String] [EnglishInflector] Fix words ending with `le`, e.g., `articles` (aleho) + * bug #57896 [Mime] Fix `RawMessage` constructor argument type (alexandre-daubois) + * bug #57887 [Uid] Ensure UuidV1 is created in lowercase (smnandre) + * bug #57870 [HttpClient] Disable HTTP/2 PUSH by default when using curl (nicolas-grekas) + * bug #57625 [DoctrineBridge] Make `EntityValueResolver` return `null` if a composite ID value is `null` (MatTheCat) + * 6.4.10 (2024-07-26) * bug #57803 [FrameworkBundle] move adding detailed JSON error messages to the validate phase (xabbuh) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 8542f255df6a1..e0cdfdc00f607 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -25,8 +25,8 @@ The Symfony Connect username in parenthesis allows to get more information - Jérémy DERUSSÉ (jderusse) - Roland Franssen - Jules Pietri (heah) - - Johannes S (johannes) - Oskar Stark (oskarstark) + - Johannes S (johannes) - Kris Wallsmith (kriswallsmith) - Jakub Zalas (jakubzalas) - Yonel Ceruto (yonelceruto) @@ -57,25 +57,25 @@ The Symfony Connect username in parenthesis allows to get more information - Vincent Langlet (deviling) - Valentin Udaltsov (vudaltsov) - Alexandre Salomé (alexandresalome) + - Simon André (simonandre) - Grégoire Paris (greg0ire) - William DURAND - ornicar - Dany Maillard (maidmaid) - - Simon André (simonandre) - Eriksen Costa - Diego Saint Esteben (dosten) - stealth35 ‏ (stealth35) - Alexander Mols (asm89) - Gábor Egyed (1ed) - Francis Besset (francisbesset) + - Mathias Arlaud (mtarld) - Titouan Galopin (tgalopin) - Pierre du Plessis (pierredup) - David Maicher (dmaicher) - Bulat Shakirzyanov (avalanche123) - - Mathias Arlaud (mtarld) + - Tomasz Kowalczyk (thunderer) - Iltar van der Berg - Miha Vrhovnik (mvrhov) - - Tomasz Kowalczyk (thunderer) - Gary PEGEOT (gary-p) - Saša Stamenković (umpirsky) - Allison Guilhem (a_guilhem) @@ -98,6 +98,7 @@ The Symfony Connect username in parenthesis allows to get more information - Ruud Kamphuis (ruudk) - Andrej Hudec (pulzarraider) - Jáchym Toušek (enumag) + - Tomas Norkūnas (norkunas) - Christian Raue - Eric Clemmons (ericclemmons) - Denis (yethee) @@ -107,7 +108,6 @@ The Symfony Connect username in parenthesis allows to get more information - Douglas Greenshields (shieldo) - Frank A. Fiebig (fafiebig) - Baldini - - Tomas Norkūnas (norkunas) - Alex Pott - Fran Moreno (franmomu) - Charles Sarrazin (csarrazi) @@ -132,10 +132,10 @@ The Symfony Connect username in parenthesis allows to get more information - Phil E. Taylor (philetaylor) - Joel Wurtz (brouznouf) - John Wards (johnwards) + - Yanick Witschi (toflar) - Théo FIDRY - Antoine Hérault (herzult) - Konstantin.Myakshin - - Yanick Witschi (toflar) - Jeroen Spee (jeroens) - Arnaud Le Blanc (arnaud-lb) - Sebastiaan Stok (sstok) @@ -155,6 +155,7 @@ The Symfony Connect username in parenthesis allows to get more information - Vladimir Tsykun (vtsykun) - Jacob Dreesen (jdreesen) - Włodzimierz Gajda (gajdaw) + - Nicolas Philippe (nikophil) - Javier Spagnoletti (phansys) - Martin Auswöger - Adrien Brault (adrienbrault) @@ -162,9 +163,9 @@ The Symfony Connect username in parenthesis allows to get more information - Teoh Han Hui (teohhanhui) - Przemysław Bogusz (przemyslaw-bogusz) - Colin Frei - - Nicolas Philippe (nikophil) - excelwebzone - Paráda József (paradajozsef) + - Maximilian Beckers (maxbeckers) - Baptiste Clavié (talus) - Alexander Schwenn (xelaris) - Fabien Pennequin (fabienpennequin) @@ -172,7 +173,6 @@ The Symfony Connect username in parenthesis allows to get more information - Malte Schlüter (maltemaltesich) - jeremyFreeAgent (jeremyfreeagent) - Michael Babker (mbabker) - - Maximilian Beckers (maxbeckers) - Valtteri R (valtzu) - Joshua Thijssen - Vasilij Dusko @@ -235,6 +235,7 @@ The Symfony Connect username in parenthesis allows to get more information - Roland Franssen :) - Romain Monteil (ker0x) - Sergey (upyx) + - Florent Morselli (spomky_) - Marco Pivetta (ocramius) - Antonio Pauletich (x-coder264) - Vincent Touzet (vincenttouzet) @@ -265,7 +266,7 @@ The Symfony Connect username in parenthesis allows to get more information - Tyson Andre - GDIBass - Samuel NELA (snela) - - Florent Morselli (spomky_) + - Baptiste Leduc (korbeil) - Vincent AUBERT (vincent) - Michael Voříšek - zairig imad (zairigimad) @@ -289,6 +290,8 @@ The Symfony Connect username in parenthesis allows to get more information - Martin Hujer (martinhujer) - Sergey Linnik (linniksa) - Richard Miller + - Quynh Xuan Nguyen (seriquynh) + - Victor Bocharsky (bocharsky_bw) - Aleksandar Jakovljevic (ajakov) - Mario A. Alvarez Garcia (nomack84) - Thomas Rabaix (rande) @@ -297,7 +300,7 @@ The Symfony Connect username in parenthesis allows to get more information - DQNEO - Chi-teck - Andre Rømcke (andrerom) - - Baptiste Leduc (korbeil) + - Patrick Landolt (scube) - Karoly Gossler (connorhu) - Timo Bakx (timobakx) - Giorgio Premi @@ -319,7 +322,6 @@ The Symfony Connect username in parenthesis allows to get more information - sun (sun) - Larry Garfield (crell) - Leo Feyer - - Victor Bocharsky (bocharsky_bw) - Nikolay Labinskiy (e-moe) - Asis Pattisahusiwa - Martin Schuhfuß (usefulthink) @@ -355,7 +357,6 @@ The Symfony Connect username in parenthesis allows to get more information - Maxime Veber (nek-) - Valentine Boineau (valentineboineau) - Rui Marinho (ruimarinho) - - Patrick Landolt (scube) - Filippo Tessarotto (slamdunk) - Jeroen Noten (jeroennoten) - Possum @@ -492,7 +493,6 @@ The Symfony Connect username in parenthesis allows to get more information - Andrew Moore (finewolf) - Bertrand Zuchuat (garfield-fr) - Marc Morera (mmoreram) - - Quynh Xuan Nguyen (seriquynh) - Gabor Toth (tgabi333) - realmfoo - Fabien S (bafs) @@ -549,6 +549,7 @@ The Symfony Connect username in parenthesis allows to get more information - Martin Herndl (herndlm) - Dmytro Borysovskyi (dmytr0) - Johann Pardanaud + - Pierre Rineau - Kai Dederichs - Pavel Kirpitsov (pavel-kirpichyov) - Robert Meijers @@ -738,6 +739,7 @@ The Symfony Connect username in parenthesis allows to get more information - Miro Michalicka - M. Vondano - Dominik Zogg + - Maximilian Zumbansen - Vadim Borodavko (javer) - Tavo Nieves J (tavoniievez) - Luc Vieillescazes (iamluc) @@ -750,7 +752,6 @@ The Symfony Connect username in parenthesis allows to get more information - Giso Stallenberg (gisostallenberg) - Rob Bast - Roberto Espinoza (respinoza) - - Pierre Rineau - Soufian EZ ZANTAR (soezz) - Marek Zajac - Adam Harvey @@ -889,6 +890,7 @@ The Symfony Connect username in parenthesis allows to get more information - Marcin Chyłek (songoq) - Ned Schwartz - Ziumin + - Daniel Tiringer - Lenar Lõhmus - Ilija Tovilo (ilijatovilo) - Sander Toonen (xatoo) @@ -925,6 +927,7 @@ The Symfony Connect username in parenthesis allows to get more information - Ninos - julien57 - Mátyás Somfai (smatyas) + - MrMicky - Bastien DURAND (deamon) - Dmitry Simushev - alcaeus @@ -1390,7 +1393,6 @@ The Symfony Connect username in parenthesis allows to get more information - Jason Woods - mwsaz - bogdan - - Daniel Tiringer - Geert De Deckere - grizlik - Derek ROTH @@ -1505,7 +1507,6 @@ The Symfony Connect username in parenthesis allows to get more information - Valérian Galliat - Sorin Pop (sorinpop) - d-ph - - MrMicky - Stewart Malik - Renan Taranto (renan-taranto) - Ninos Ego @@ -1522,6 +1523,7 @@ The Symfony Connect username in parenthesis allows to get more information - Amaury Leroux de Lens (amo__) - Rene de Lima Barbosa (renedelima) - Christian Jul Jensen + - Lukas Kaltenbach - Alexandre GESLIN - The Whole Life to Learn - Pierre Tondereau @@ -1772,6 +1774,7 @@ The Symfony Connect username in parenthesis allows to get more information - benatespina (benatespina) - Denis Kop - Fabrice Locher + - Konstantin Chigakov - Kamil Szalewski (szal1k) - Jean-Guilhem Rouel (jean-gui) - Yoann MOROCUTTI @@ -1830,7 +1833,6 @@ The Symfony Connect username in parenthesis allows to get more information - Atthaphon Urairat - Benoit Garret - HellFirePvP - - Maximilian Zumbansen - Maximilian Ruta (deltachaos) - Jon Green (jontjs) - Jakub Sacha @@ -1898,6 +1900,7 @@ The Symfony Connect username in parenthesis allows to get more information - Albert Ganiev (helios-ag) - Neil Katin - Oleg Mifle + - V1nicius00 - David Otton - Will Donohoe - peter @@ -1966,6 +1969,7 @@ The Symfony Connect username in parenthesis allows to get more information - Roger Webb - Dmitriy Simushev - Pawel Smolinski + - Yury (daffox) - John Espiritu (johnillo) - Tomasz (timitao) - Nguyen Tuan Minh (tuanminhgp) @@ -2407,6 +2411,7 @@ The Symfony Connect username in parenthesis allows to get more information - Alex Silcock - Raphael Hardt - Ivan Nemets + - Dave Long - Qingshan Luo - Michael Olšavský - Ergie Gonzaga @@ -2526,6 +2531,7 @@ The Symfony Connect username in parenthesis allows to get more information - Wouter de Wild - Peter Potrowl - povilas + - andreybolonin1989@gmail.com - Gavin Staniforth - bahram - Alessandro Tagliapietra (alex88) @@ -2540,6 +2546,7 @@ The Symfony Connect username in parenthesis allows to get more information - Tiago Garcia (tiagojsag) - Artiom - Jakub Simon + - Petrisor Ciprian Daniel - Eviljeks - robin.de.croock - Brandon Antonio Lorenzo @@ -2706,6 +2713,7 @@ The Symfony Connect username in parenthesis allows to get more information - Nil Borodulia - Adam Katz - Almog Baku (almogbaku) + - Boris Grishenko (arczinosek) - Arrakis (arrakis) - Danil Khaliullin (bifidokk) - Benjamin Schultz (bschultz) @@ -2893,6 +2901,7 @@ The Symfony Connect username in parenthesis allows to get more information - Markus Staab - Ryan Hendrickson - Valentin + - Gerard - Sören Bernstein - michael.kubovic - devel @@ -2944,6 +2953,7 @@ The Symfony Connect username in parenthesis allows to get more information - Maxime Corteel (mcorteel) - Dan Patrick (mdpatrick) - Mathieu MARCHOIS (mmar) + - Nei Rauni Santos (nrauni) - Geoffrey Monte (numerogeek) - Martijn Boers (plebian) - Plamen Mishev (pmishev) @@ -3425,6 +3435,7 @@ The Symfony Connect username in parenthesis allows to get more information - Bastien THOMAS - Shaun Simmons - Pierre-Louis LAUNAY + - Arseny Razin - A. Pauly - djama - Benjamin Rosenberger @@ -3526,6 +3537,7 @@ The Symfony Connect username in parenthesis allows to get more information - Matthias Larisch - Maxime P - Sean Templeton + - Willem Mouwen - db306 - Michaël VEROUX - Julia @@ -3621,6 +3633,7 @@ The Symfony Connect username in parenthesis allows to get more information - Jérémy (libertjeremy) - Mehdi Achour (machour) - Mamikon Arakelyan (mamikon) + - Mark Schmale (masch) - Matt Ketmo (mattketmo) - Moritz Borgmann (mborgmann) - Matt Drollette (mdrollette) diff --git a/src/Symfony/Bridge/Doctrine/ArgumentResolver/EntityValueResolver.php b/src/Symfony/Bridge/Doctrine/ArgumentResolver/EntityValueResolver.php index bdf975b32befd..c3cc1c8aa496c 100644 --- a/src/Symfony/Bridge/Doctrine/ArgumentResolver/EntityValueResolver.php +++ b/src/Symfony/Bridge/Doctrine/ArgumentResolver/EntityValueResolver.php @@ -104,6 +104,9 @@ private function find(ObjectManager $manager, Request $request, MapEntity $optio if (false === $id || null === $id) { return $id; } + if (\is_array($id) && \in_array(null, $id, true)) { + return null; + } if ($options->evictCache && $manager instanceof EntityManagerInterface) { $cacheProvider = $manager->getCache(); diff --git a/src/Symfony/Bridge/Doctrine/SchemaListener/LockStoreSchemaListener.php b/src/Symfony/Bridge/Doctrine/SchemaListener/LockStoreSchemaListener.php index a85d159df837b..c4c3b0b7ffcad 100644 --- a/src/Symfony/Bridge/Doctrine/SchemaListener/LockStoreSchemaListener.php +++ b/src/Symfony/Bridge/Doctrine/SchemaListener/LockStoreSchemaListener.php @@ -12,7 +12,6 @@ namespace Symfony\Bridge\Doctrine\SchemaListener; use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs; -use Symfony\Component\Lock\Exception\InvalidArgumentException; use Symfony\Component\Lock\PersistingStoreInterface; use Symfony\Component\Lock\Store\DoctrineDbalStore; @@ -30,20 +29,12 @@ public function postGenerateSchema(GenerateSchemaEventArgs $event): void { $connection = $event->getEntityManager()->getConnection(); - $storesIterator = new \ArrayIterator($this->stores); - while ($storesIterator->valid()) { - try { - $store = $storesIterator->current(); - if (!$store instanceof DoctrineDbalStore) { - continue; - } - - $store->configureSchema($event->getSchema(), $this->getIsSameDatabaseChecker($connection)); - } catch (InvalidArgumentException) { - // no-op + foreach ($this->stores as $store) { + if (!$store instanceof DoctrineDbalStore) { + continue; } - $storesIterator->next(); + $store->configureSchema($event->getSchema(), $this->getIsSameDatabaseChecker($connection)); } } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/ArgumentResolver/EntityValueResolverTest.php b/src/Symfony/Bridge/Doctrine/Tests/ArgumentResolver/EntityValueResolverTest.php index 749e9b7792144..6ae01a1c11a8a 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/ArgumentResolver/EntityValueResolverTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/ArgumentResolver/EntityValueResolverTest.php @@ -144,6 +144,20 @@ public function testResolveWithNullId() $this->assertSame([null], $resolver->resolve($request, $argument)); } + public function testResolveWithArrayIdNullValue() + { + $manager = $this->createMock(ObjectManager::class); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry); + + $request = new Request(); + $request->attributes->set('nullValue', null); + + $argument = $this->createArgument(entity: new MapEntity(id: ['nullValue']), isNullable: true,); + + $this->assertSame([null], $resolver->resolve($request, $argument)); + } + public function testResolveWithConversionFailedException() { $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); diff --git a/src/Symfony/Bridge/Doctrine/Tests/SchemaListener/LockStoreSchemaListenerTest.php b/src/Symfony/Bridge/Doctrine/Tests/SchemaListener/LockStoreSchemaListenerTest.php index 6f23d680feb9f..6fd86a46c84e5 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/SchemaListener/LockStoreSchemaListenerTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/SchemaListener/LockStoreSchemaListenerTest.php @@ -17,7 +17,6 @@ use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs; use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\SchemaListener\LockStoreSchemaListener; -use Symfony\Component\Lock\Exception\InvalidArgumentException; use Symfony\Component\Lock\Store\DoctrineDbalStore; class LockStoreSchemaListenerTest extends TestCase @@ -37,23 +36,7 @@ public function testPostGenerateSchemaLockPdo() ->method('configureSchema') ->with($schema, fn () => true); - $subscriber = new LockStoreSchemaListener([$lockStore]); - $subscriber->postGenerateSchema($event); - } - - public function testPostGenerateSchemaWithInvalidLockStore() - { - $entityManager = $this->createMock(EntityManagerInterface::class); - $entityManager->expects($this->once()) - ->method('getConnection') - ->willReturn($this->createMock(Connection::class)); - $event = new GenerateSchemaEventArgs($entityManager, new Schema()); - - $subscriber = new LockStoreSchemaListener((static function (): \Generator { - yield $this->createMock(DoctrineDbalStore::class); - - throw new InvalidArgumentException('Unsupported Connection'); - })()); + $subscriber = new LockStoreSchemaListener((static fn () => yield $lockStore)()); $subscriber->postGenerateSchema($event); } } diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php index 3249f12e59e32..c67eca0c6aa6d 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php @@ -418,7 +418,7 @@ private static function hasColorSupport() } // Follow https://no-color.org/ - if ('' !== ($_SERVER['NO_COLOR'] ?? getenv('NO_COLOR') ?: '')) { + if ('' !== (($_SERVER['NO_COLOR'] ?? getenv('NO_COLOR'))[0] ?? '')) { return false; } diff --git a/src/Symfony/Bridge/PsrHttpMessage/Factory/HttpFoundationFactory.php b/src/Symfony/Bridge/PsrHttpMessage/Factory/HttpFoundationFactory.php index b1ee25a40df45..cad798e5fc91b 100644 --- a/src/Symfony/Bridge/PsrHttpMessage/Factory/HttpFoundationFactory.php +++ b/src/Symfony/Bridge/PsrHttpMessage/Factory/HttpFoundationFactory.php @@ -132,89 +132,12 @@ public function createResponse(ResponseInterface $psrResponse, bool $streamed = $response->setProtocolVersion($psrResponse->getProtocolVersion()); foreach ($cookies as $cookie) { - $response->headers->setCookie($this->createCookie($cookie)); + $response->headers->setCookie(Cookie::fromString($cookie)); } return $response; } - /** - * Creates a Cookie instance from a cookie string. - * - * Some snippets have been taken from the Guzzle project: https://github.com/guzzle/guzzle/blob/5.3/src/Cookie/SetCookie.php#L34 - * - * @throws \InvalidArgumentException - */ - private function createCookie(string $cookie): Cookie - { - foreach (explode(';', $cookie) as $part) { - $part = trim($part); - - $data = explode('=', $part, 2); - $name = $data[0]; - $value = isset($data[1]) ? trim($data[1], " \n\r\t\0\x0B\"") : null; - - if (!isset($cookieName)) { - $cookieName = $name; - $cookieValue = $value; - - continue; - } - - if ('expires' === strtolower($name) && null !== $value) { - $cookieExpire = new \DateTime($value); - - continue; - } - - if ('path' === strtolower($name) && null !== $value) { - $cookiePath = $value; - - continue; - } - - if ('domain' === strtolower($name) && null !== $value) { - $cookieDomain = $value; - - continue; - } - - if ('secure' === strtolower($name)) { - $cookieSecure = true; - - continue; - } - - if ('httponly' === strtolower($name)) { - $cookieHttpOnly = true; - - continue; - } - - if ('samesite' === strtolower($name) && null !== $value) { - $samesite = $value; - - continue; - } - } - - if (!isset($cookieName)) { - throw new \InvalidArgumentException('The value of the Set-Cookie header is malformed.'); - } - - return new Cookie( - $cookieName, - $cookieValue, - $cookieExpire ?? 0, - $cookiePath ?? '/', - $cookieDomain ?? null, - isset($cookieSecure), - isset($cookieHttpOnly), - true, - $samesite ?? null - ); - } - private function createStreamedResponseCallback(StreamInterface $body): callable { return function () use ($body) { diff --git a/src/Symfony/Bridge/Twig/Node/DumpNode.php b/src/Symfony/Bridge/Twig/Node/DumpNode.php index 9b736da23c44e..17463c71a182d 100644 --- a/src/Symfony/Bridge/Twig/Node/DumpNode.php +++ b/src/Symfony/Bridge/Twig/Node/DumpNode.php @@ -11,6 +11,7 @@ namespace Symfony\Bridge\Twig\Node; +use Twig\Attribute\FirstClassTwigCallableReady; use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Node\Node; @@ -30,7 +31,12 @@ public function __construct(string $varPrefix, ?Node $values, int $lineno, ?stri $nodes['values'] = $values; } - parent::__construct($nodes, [], $lineno, $tag); + if (class_exists(FirstClassTwigCallableReady::class)) { + parent::__construct($nodes, [], $lineno); + } else { + parent::__construct($nodes, [], $lineno, $tag); + } + $this->varPrefix = $varPrefix; } diff --git a/src/Symfony/Bridge/Twig/Node/FormThemeNode.php b/src/Symfony/Bridge/Twig/Node/FormThemeNode.php index e38557ceacbce..1d077097f119f 100644 --- a/src/Symfony/Bridge/Twig/Node/FormThemeNode.php +++ b/src/Symfony/Bridge/Twig/Node/FormThemeNode.php @@ -12,6 +12,7 @@ namespace Symfony\Bridge\Twig\Node; use Symfony\Component\Form\FormRenderer; +use Twig\Attribute\FirstClassTwigCallableReady; use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Node\Node; @@ -24,7 +25,11 @@ final class FormThemeNode extends Node { public function __construct(Node $form, Node $resources, int $lineno, ?string $tag = null, bool $only = false) { - parent::__construct(['form' => $form, 'resources' => $resources], ['only' => $only], $lineno, $tag); + if (class_exists(FirstClassTwigCallableReady::class)) { + parent::__construct(['form' => $form, 'resources' => $resources], ['only' => $only], $lineno); + } else { + parent::__construct(['form' => $form, 'resources' => $resources], ['only' => $only], $lineno, $tag); + } } public function compile(Compiler $compiler): void diff --git a/src/Symfony/Bridge/Twig/Node/StopwatchNode.php b/src/Symfony/Bridge/Twig/Node/StopwatchNode.php index 9a69d4eff39fc..239d1ca654bca 100644 --- a/src/Symfony/Bridge/Twig/Node/StopwatchNode.php +++ b/src/Symfony/Bridge/Twig/Node/StopwatchNode.php @@ -11,6 +11,7 @@ namespace Symfony\Bridge\Twig\Node; +use Twig\Attribute\FirstClassTwigCallableReady; use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Node\Expression\AssignNameExpression; @@ -26,7 +27,11 @@ final class StopwatchNode extends Node { public function __construct(Node $name, Node $body, AssignNameExpression $var, int $lineno = 0, ?string $tag = null) { - parent::__construct(['body' => $body, 'name' => $name, 'var' => $var], [], $lineno, $tag); + if (class_exists(FirstClassTwigCallableReady::class)) { + parent::__construct(['body' => $body, 'name' => $name, 'var' => $var], [], $lineno); + } else { + parent::__construct(['body' => $body, 'name' => $name, 'var' => $var], [], $lineno, $tag); + } } public function compile(Compiler $compiler): void diff --git a/src/Symfony/Bridge/Twig/Node/TransDefaultDomainNode.php b/src/Symfony/Bridge/Twig/Node/TransDefaultDomainNode.php index d24d7f75f236b..28cb6f1b4b2d3 100644 --- a/src/Symfony/Bridge/Twig/Node/TransDefaultDomainNode.php +++ b/src/Symfony/Bridge/Twig/Node/TransDefaultDomainNode.php @@ -11,6 +11,7 @@ namespace Symfony\Bridge\Twig\Node; +use Twig\Attribute\FirstClassTwigCallableReady; use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Node\Expression\AbstractExpression; @@ -24,7 +25,11 @@ final class TransDefaultDomainNode extends Node { public function __construct(AbstractExpression $expr, int $lineno = 0, ?string $tag = null) { - parent::__construct(['expr' => $expr], [], $lineno, $tag); + if (class_exists(FirstClassTwigCallableReady::class)) { + parent::__construct(['expr' => $expr], [], $lineno); + } else { + parent::__construct(['expr' => $expr], [], $lineno, $tag); + } } public function compile(Compiler $compiler): void diff --git a/src/Symfony/Bridge/Twig/Node/TransNode.php b/src/Symfony/Bridge/Twig/Node/TransNode.php index 0224d46ae0e50..a711a7cab59cb 100644 --- a/src/Symfony/Bridge/Twig/Node/TransNode.php +++ b/src/Symfony/Bridge/Twig/Node/TransNode.php @@ -11,6 +11,7 @@ namespace Symfony\Bridge\Twig\Node; +use Twig\Attribute\FirstClassTwigCallableReady; use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Node\Expression\AbstractExpression; @@ -42,7 +43,11 @@ public function __construct(Node $body, ?Node $domain = null, ?AbstractExpressio $nodes['locale'] = $locale; } - parent::__construct($nodes, [], $lineno, $tag); + if (class_exists(FirstClassTwigCallableReady::class)) { + parent::__construct($nodes, [], $lineno); + } else { + parent::__construct($nodes, [], $lineno, $tag); + } } public function compile(Compiler $compiler): void diff --git a/src/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php b/src/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php index 3449751b1ff92..4ebccb4e23d1d 100644 --- a/src/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php +++ b/src/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php @@ -61,7 +61,7 @@ public function enterNode(Node $node, Environment $env): Node return $node; } - if ($node instanceof FilterExpression && 'trans' === $node->getNode('filter')->getAttribute('value')) { + if ($node instanceof FilterExpression && 'trans' === ($node->hasAttribute('twig_callable') ? $node->getAttribute('twig_callable')->getName() : $node->getNode('filter')->getAttribute('value'))) { $arguments = $node->getNode('arguments'); if ($this->isNamedArguments($arguments)) { if (!$arguments->hasNode('domain') && !$arguments->hasNode(1)) { diff --git a/src/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php b/src/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php index c44a8894469f3..f2b8f197e4a68 100644 --- a/src/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php +++ b/src/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php @@ -57,7 +57,7 @@ public function enterNode(Node $node, Environment $env): Node if ( $node instanceof FilterExpression - && 'trans' === $node->getNode('filter')->getAttribute('value') + && 'trans' === ($node->hasAttribute('twig_callable') ? $node->getAttribute('twig_callable')->getName() : $node->getNode('filter')->getAttribute('value')) && $node->getNode('node') instanceof ConstantExpression ) { // extract constant nodes with a trans filter @@ -85,7 +85,7 @@ public function enterNode(Node $node, Environment $env): Node ]; } elseif ( $node instanceof FilterExpression - && 'trans' === $node->getNode('filter')->getAttribute('value') + && 'trans' === ($node->hasAttribute('twig_callable') ? $node->getAttribute('twig_callable')->getName() : $node->getNode('filter')->getAttribute('value')) && $node->getNode('node') instanceof ConcatBinary && $message = $this->getConcatValueFromNode($node->getNode('node'), null) ) { diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php index 0c3d29508d202..9b7eb0b1165c8 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php @@ -71,7 +71,7 @@ public function testGenerateFragmentUri() 'index' => sprintf(<< true, 'cache' => false]); $twig->addExtension(new HttpKernelExtension()); @@ -81,7 +81,7 @@ public function testGenerateFragmentUri() ]); $twig->addRuntimeLoader($loader); - $this->assertSame('/_fragment?_hash=PP8%2FeEbn1pr27I9wmag%2FM6jYGVwUZ0l2h0vhh2OJ6CI%3D&_path=template%3Dfoo.html.twig%26_format%3Dhtml%26_locale%3Den%26_controller%3DSymfonyBundleFrameworkBundleControllerTemplateController%253A%253AtemplateAction', $twig->render('index')); + $this->assertSame('/_fragment?_hash=XCg0hX8QzSwik8Xuu9aMXhoCeI4oJOob7lUVacyOtyY%3D&_path=template%3Dfoo.html.twig%26_format%3Dhtml%26_locale%3Den%26_controller%3DSymfony%255CBundle%255CFrameworkBundle%255CController%255CTemplateController%253A%253AtemplateAction', $twig->render('index')); } protected function getFragmentHandler($returnOrException): FragmentHandler diff --git a/src/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php b/src/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php index b259990e0b7ad..c2fdb4e778541 100644 --- a/src/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode; +use Twig\Attribute\FirstClassTwigCallableReady; use Twig\Compiler; use Twig\Environment; use Twig\Extension\CoreExtension; @@ -22,6 +23,7 @@ use Twig\Node\Expression\ConstantExpression; use Twig\Node\Expression\NameExpression; use Twig\Node\Node; +use Twig\TwigFunction; class SearchAndRenderBlockNodeTest extends TestCase { @@ -31,7 +33,11 @@ public function testCompileWidget() new NameExpression('form', 0), ]); - $node = new SearchAndRenderBlockNode('form_widget', $arguments, 0); + if (class_exists(FirstClassTwigCallableReady::class)) { + $node = new SearchAndRenderBlockNode(new TwigFunction('form_widget'), $arguments, 0); + } else { + $node = new SearchAndRenderBlockNode('form_widget', $arguments, 0); + } $compiler = new Compiler(new Environment($this->createMock(LoaderInterface::class))); @@ -54,7 +60,11 @@ public function testCompileWidgetWithVariables() ], 0), ]); - $node = new SearchAndRenderBlockNode('form_widget', $arguments, 0); + if (class_exists(FirstClassTwigCallableReady::class)) { + $node = new SearchAndRenderBlockNode(new TwigFunction('form_widget'), $arguments, 0); + } else { + $node = new SearchAndRenderBlockNode('form_widget', $arguments, 0); + } $compiler = new Compiler(new Environment($this->createMock(LoaderInterface::class))); @@ -74,7 +84,11 @@ public function testCompileLabelWithLabel() new ConstantExpression('my label', 0), ]); - $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); + if (class_exists(FirstClassTwigCallableReady::class)) { + $node = new SearchAndRenderBlockNode(new TwigFunction('form_label'), $arguments, 0); + } else { + $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); + } $compiler = new Compiler(new Environment($this->createMock(LoaderInterface::class))); @@ -94,7 +108,11 @@ public function testCompileLabelWithNullLabel() new ConstantExpression(null, 0), ]); - $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); + if (class_exists(FirstClassTwigCallableReady::class)) { + $node = new SearchAndRenderBlockNode(new TwigFunction('form_label'), $arguments, 0); + } else { + $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); + } $compiler = new Compiler(new Environment($this->createMock(LoaderInterface::class))); @@ -116,7 +134,11 @@ public function testCompileLabelWithEmptyStringLabel() new ConstantExpression('', 0), ]); - $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); + if (class_exists(FirstClassTwigCallableReady::class)) { + $node = new SearchAndRenderBlockNode(new TwigFunction('form_label'), $arguments, 0); + } else { + $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); + } $compiler = new Compiler(new Environment($this->createMock(LoaderInterface::class))); @@ -137,7 +159,11 @@ public function testCompileLabelWithDefaultLabel() new NameExpression('form', 0), ]); - $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); + if (class_exists(FirstClassTwigCallableReady::class)) { + $node = new SearchAndRenderBlockNode(new TwigFunction('form_label'), $arguments, 0); + } else { + $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); + } $compiler = new Compiler(new Environment($this->createMock(LoaderInterface::class))); @@ -161,7 +187,11 @@ public function testCompileLabelWithAttributes() ], 0), ]); - $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); + if (class_exists(FirstClassTwigCallableReady::class)) { + $node = new SearchAndRenderBlockNode(new TwigFunction('form_label'), $arguments, 0); + } else { + $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); + } $compiler = new Compiler(new Environment($this->createMock(LoaderInterface::class))); @@ -190,7 +220,11 @@ public function testCompileLabelWithLabelAndAttributes() ], 0), ]); - $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); + if (class_exists(FirstClassTwigCallableReady::class)) { + $node = new SearchAndRenderBlockNode(new TwigFunction('form_label'), $arguments, 0); + } else { + $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); + } $compiler = new Compiler(new Environment($this->createMock(LoaderInterface::class))); @@ -218,7 +252,11 @@ public function testCompileLabelWithLabelThatEvaluatesToNull() ), ]); - $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); + if (class_exists(FirstClassTwigCallableReady::class)) { + $node = new SearchAndRenderBlockNode(new TwigFunction('form_label'), $arguments, 0); + } else { + $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); + } $compiler = new Compiler(new Environment($this->createMock(LoaderInterface::class))); @@ -256,7 +294,11 @@ public function testCompileLabelWithLabelThatEvaluatesToNullAndAttributes() ], 0), ]); - $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); + if (class_exists(FirstClassTwigCallableReady::class)) { + $node = new SearchAndRenderBlockNode(new TwigFunction('form_label'), $arguments, 0); + } else { + $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); + } $compiler = new Compiler(new Environment($this->createMock(LoaderInterface::class))); diff --git a/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.php b/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.php index bf073602583f7..be26c9b425efc 100644 --- a/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.php +++ b/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\NodeVisitor\TranslationNodeVisitor; +use Twig\Attribute\FirstClassTwigCallableReady; use Twig\Environment; use Twig\Loader\LoaderInterface; use Twig\Node\Expression\ArrayExpression; @@ -20,6 +21,8 @@ use Twig\Node\Expression\FilterExpression; use Twig\Node\Expression\NameExpression; use Twig\Node\Node; +use Twig\TwigFilter; +use Twig\TwigFunction; class TranslationNodeVisitorTest extends TestCase { @@ -38,15 +41,27 @@ public function testMessageExtractionWithInvalidDomainNode() { $message = 'new key'; - $node = new FilterExpression( - new ConstantExpression($message, 0), - new ConstantExpression('trans', 0), - new Node([ - new ArrayExpression([], 0), - new NameExpression('variable', 0), - ]), - 0 - ); + if (class_exists(FirstClassTwigCallableReady::class)) { + $node = new FilterExpression( + new ConstantExpression($message, 0), + new TwigFilter('trans'), + new Node([ + new ArrayExpression([], 0), + new NameExpression('variable', 0), + ]), + 0 + ); + } else { + $node = new FilterExpression( + new ConstantExpression($message, 0), + new ConstantExpression('trans', 0), + new Node([ + new ArrayExpression([], 0), + new NameExpression('variable', 0), + ]), + 0 + ); + } $this->testMessagesExtraction($node, [[$message, TranslationNodeVisitor::UNDEFINED_DOMAIN]]); } diff --git a/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TwigNodeProvider.php b/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TwigNodeProvider.php index 69311afdc824d..7a79c34130016 100644 --- a/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TwigNodeProvider.php +++ b/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TwigNodeProvider.php @@ -13,6 +13,7 @@ use Symfony\Bridge\Twig\Node\TransDefaultDomainNode; use Symfony\Bridge\Twig\Node\TransNode; +use Twig\Attribute\FirstClassTwigCallableReady; use Twig\Node\BodyNode; use Twig\Node\Expression\ArrayExpression; use Twig\Node\Expression\ConstantExpression; @@ -20,13 +21,14 @@ use Twig\Node\ModuleNode; use Twig\Node\Node; use Twig\Source; +use Twig\TwigFilter; class TwigNodeProvider { public static function getModule($content) { return new ModuleNode( - new ConstantExpression($content, 0), + new BodyNode([new ConstantExpression($content, 0)]), null, new ArrayExpression([], 0), new ArrayExpression([], 0), @@ -45,9 +47,18 @@ public static function getTransFilter($message, $domain = null, $arguments = nul ] : []; } + if (!class_exists(FirstClassTwigCallableReady::class)) { + return new FilterExpression( + new ConstantExpression($message, 0), + new ConstantExpression('trans', 0), + new Node($arguments), + 0 + ); + } + return new FilterExpression( new ConstantExpression($message, 0), - new ConstantExpression('trans', 0), + new TwigFilter('trans'), new Node($arguments), 0 ); diff --git a/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php b/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php index 41504050f74f8..c9c0ce80c1b2d 100644 --- a/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php +++ b/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\Node\FormThemeNode; use Symfony\Bridge\Twig\TokenParser\FormThemeTokenParser; +use Twig\Attribute\FirstClassTwigCallableReady; use Twig\Environment; use Twig\Loader\LoaderInterface; use Twig\Node\Expression\ArrayExpression; @@ -35,6 +36,10 @@ public function testCompile($source, $expected) $stream = $env->tokenize($source); $parser = new Parser($env); + if (class_exists(FirstClassTwigCallableReady::class)) { + $expected->setNodeTag('form_theme'); + } + $expected->setSourceContext($source); $this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode(0)); diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd b/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd index bf15a5db164ec..670974a1f00c6 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd @@ -92,7 +92,7 @@ - + @@ -151,7 +151,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php b/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php index 6f1bdfcdd4892..21e5b8aa68279 100644 --- a/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php +++ b/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php @@ -72,14 +72,7 @@ private function getFirewallContext(Request $request): ?FirewallContext if (null === $requestMatcher || $requestMatcher->matches($request)) { $request->attributes->set('_firewall_context', $contextId); - /** @var FirewallContext $context */ - $context = $this->container->get($contextId); - - if ($context->getConfig()?->isStateless() && !$request->attributes->has('_stateless')) { - $request->attributes->set('_stateless', true); - } - - return $context; + return $this->container->get($contextId); } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/Authenticator/CustomAuthenticator.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/Authenticator/CustomAuthenticator.php new file mode 100644 index 0000000000000..6169779ad21ab --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/Authenticator/CustomAuthenticator.php @@ -0,0 +1,29 @@ +children() + ->scalarNode('foo')->defaultValue('bar')->end() + ->end() + ; + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_authenticator_under_own_namespace.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_authenticator_under_own_namespace.xml new file mode 100644 index 0000000000000..c520645172972 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_authenticator_under_own_namespace.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_authenticator_under_security_namespace.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_authenticator_under_security_namespace.xml new file mode 100644 index 0000000000000..7bd3790fc0d5f --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_authenticator_under_security_namespace.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_provider_under_own_namespace.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_provider_under_own_namespace.xml new file mode 100644 index 0000000000000..e0b1119b522d8 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_provider_under_own_namespace.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_provider_under_security_namespace.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_provider_under_security_namespace.xml new file mode 100644 index 0000000000000..647a9b234218b --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_provider_under_security_namespace.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCustomAuthenticatorTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCustomAuthenticatorTest.php new file mode 100644 index 0000000000000..de3db233a2060 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCustomAuthenticatorTest.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension; +use Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Fixtures\Authenticator\CustomAuthenticator; +use Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Fixtures\UserProvider\CustomProvider; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; + +class XmlCustomAuthenticatorTest extends TestCase +{ + /** + * @dataProvider provideXmlConfigurationFile + */ + public function testCustomProviderElement(string $configurationFile) + { + $container = new ContainerBuilder(); + $container->setParameter('kernel.debug', false); + $container->register('cache.system', \stdClass::class); + + $security = new SecurityExtension(); + $security->addAuthenticatorFactory(new CustomAuthenticator()); + $container->registerExtension($security); + + (new XmlFileLoader($container, new FileLocator(__DIR__.'/Fixtures/xml')))->load($configurationFile); + + $container->getCompilerPassConfig()->setRemovingPasses([]); + $container->getCompilerPassConfig()->setAfterRemovingPasses([]); + $container->compile(); + + $this->addToAssertionCount(1); + } + + public static function provideXmlConfigurationFile(): iterable + { + yield 'Custom authenticator element under SecurityBundle’s namespace' => ['custom_authenticator_under_security_namespace.xml']; + yield 'Custom authenticator element under its own namespace' => ['custom_authenticator_under_own_namespace.xml']; + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCustomProviderTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCustomProviderTest.php new file mode 100644 index 0000000000000..a3f59fc299a24 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCustomProviderTest.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension; +use Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Fixtures\UserProvider\CustomProvider; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; + +class XmlCustomProviderTest extends TestCase +{ + /** + * @dataProvider provideXmlConfigurationFile + */ + public function testCustomProviderElement(string $configurationFile) + { + $container = new ContainerBuilder(); + $container->setParameter('kernel.debug', false); + $container->register('cache.system', \stdClass::class); + + $security = new SecurityExtension(); + $security->addUserProviderFactory(new CustomProvider()); + $container->registerExtension($security); + + (new XmlFileLoader($container, new FileLocator(__DIR__.'/Fixtures/xml')))->load($configurationFile); + + $container->getCompilerPassConfig()->setRemovingPasses([]); + $container->getCompilerPassConfig()->setAfterRemovingPasses([]); + $container->compile(); + + $this->addToAssertionCount(1); + } + + public static function provideXmlConfigurationFile(): iterable + { + yield 'Custom provider element under SecurityBundle’s namespace' => ['custom_provider_under_security_namespace.xml']; + yield 'Custom provider element under its own namespace' => ['custom_provider_under_own_namespace.xml']; + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallMapTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallMapTest.php index fdf9c3d53a3c7..81c85ad76c204 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallMapTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallMapTest.php @@ -63,7 +63,7 @@ public function testGetListeners(Request $request, bool $expectedState) $firewallContext = $this->createMock(FirewallContext::class); $firewallConfig = new FirewallConfig('main', 'user_checker', null, true, true); - $firewallContext->expects($this->exactly(2))->method('getConfig')->willReturn($firewallConfig); + $firewallContext->expects($this->once())->method('getConfig')->willReturn($firewallConfig); $listener = function () {}; $firewallContext->expects($this->once())->method('getListeners')->willReturn([$listener]); @@ -93,7 +93,7 @@ public function testGetListeners(Request $request, bool $expectedState) public static function providesStatefulStatelessRequests(): \Generator { - yield [new Request(), true]; + yield [new Request(), false]; yield [new Request(attributes: ['_stateless' => false]), false]; yield [new Request(attributes: ['_stateless' => true]), true]; } diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index 7d28878cc62fc..95d2ce9570045 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -21,7 +21,7 @@ "ext-xml": "*", "symfony/clock": "^6.3|^7.0", "symfony/config": "^6.1|^7.0", - "symfony/dependency-injection": "^6.2|^7.0", + "symfony/dependency-injection": "^6.4.11|^7.1.4", "symfony/deprecation-contracts": "^2.5|^3", "symfony/event-dispatcher": "^5.4|^6.0|^7.0", "symfony/http-kernel": "^6.2", diff --git a/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php b/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php index f9f7686dcb249..f4e46b0a0340f 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php @@ -83,10 +83,10 @@ public function panelAction(string $token): Response */ private function getTraces(RequestDataCollector $request, string $method): array { - $traceRequest = Request::create( - $request->getPathInfo(), - $request->getRequestServer(true)->get('REQUEST_METHOD'), - \in_array($request->getMethod(), ['DELETE', 'PATCH', 'POST', 'PUT'], true) ? $request->getRequestRequest()->all() : $request->getRequestQuery()->all(), + $traceRequest = new Request( + $request->getRequestQuery()->all(), + $request->getRequestRequest()->all(), + $request->getRequestAttributes()->all(), $request->getRequestCookies(true)->all(), [], $request->getRequestServer(true)->all() diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/Controller/RouterControllerTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/Controller/RouterControllerTest.php new file mode 100644 index 0000000000000..07d5a0739e393 --- /dev/null +++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/Controller/RouterControllerTest.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\WebProfilerBundle\Tests\Controller; + +use Symfony\Bundle\FrameworkBundle\KernelBrowser; +use Symfony\Bundle\FrameworkBundle\Routing\Router; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; +use Symfony\Bundle\WebProfilerBundle\Tests\Functional\WebProfilerBundleKernel; +use Symfony\Component\DomCrawler\Crawler; +use Symfony\Component\Routing\Route; + +class RouterControllerTest extends WebTestCase +{ + public function testFalseNegativeTrace() + { + $path = '/foo/bar:123/baz'; + + $kernel = new WebProfilerBundleKernel(); + $client = new KernelBrowser($kernel); + $client->disableReboot(); + $client->getKernel()->boot(); + + /** @var Router $router */ + $router = $client->getContainer()->get('router'); + $router->getRouteCollection()->add('route1', new Route($path)); + + $client->request('GET', $path); + + $crawler = $client->request('GET', '/_profiler/latest?panel=router&type=request'); + + $matchedRouteCell = $crawler + ->filter('#router-logs .status-success td') + ->reduce(function (Crawler $td) use ($path): bool { + return $td->text() === $path; + }); + + $this->assertSame(1, $matchedRouteCell->count()); + } +} diff --git a/src/Symfony/Component/AssetMapper/ImportMap/Resolver/JsDelivrEsmResolver.php b/src/Symfony/Component/AssetMapper/ImportMap/Resolver/JsDelivrEsmResolver.php index 0788bbb77385c..93338a1b63201 100644 --- a/src/Symfony/Component/AssetMapper/ImportMap/Resolver/JsDelivrEsmResolver.php +++ b/src/Symfony/Component/AssetMapper/ImportMap/Resolver/JsDelivrEsmResolver.php @@ -28,7 +28,7 @@ final class JsDelivrEsmResolver implements PackageResolverInterface public const URL_PATTERN_DIST = self::URL_PATTERN_DIST_CSS.'/+esm'; public const URL_PATTERN_ENTRYPOINT = 'https://data.jsdelivr.com/v1/packages/npm/%s@%s/entrypoints'; - public const IMPORT_REGEX = '#(?:import\s*(?:\w+,)?(?:(?:\{[^}]*\}|\w+|\*\s*as\s+\w+)\s*\bfrom\s*)?|export\s*(?:\{[^}]*\}|\*)\s*from\s*)("/npm/((?:@[^/]+/)?[^@]+?)(?:@([^/]+))?((?:/[^/]+)*?)/\+esm")#'; + public const IMPORT_REGEX = '#(?:import\s*(?:[\w$]+,)?(?:(?:\{[^}]*\}|[\w$]+|\*\s*as\s+[\w$]+)\s*\bfrom\s*)?|export\s*(?:\{[^}]*\}|\*)\s*from\s*)("/npm/((?:@[^/]+/)?[^@]+?)(?:@([^/]+))?((?:/[^/]+)*?)/\+esm")#'; private const ES_MODULE_SHIMS = 'es-module-shims'; diff --git a/src/Symfony/Component/AssetMapper/Tests/ImportMap/Resolver/JsDelivrEsmResolverTest.php b/src/Symfony/Component/AssetMapper/Tests/ImportMap/Resolver/JsDelivrEsmResolverTest.php index f5fb90d2c90c9..8b7d82c8c6f06 100644 --- a/src/Symfony/Component/AssetMapper/Tests/ImportMap/Resolver/JsDelivrEsmResolverTest.php +++ b/src/Symfony/Component/AssetMapper/Tests/ImportMap/Resolver/JsDelivrEsmResolverTest.php @@ -686,6 +686,13 @@ public static function provideImportRegex(): iterable ['datatables.net-select', '1.7.0'], ], ]; + + yield 'import with name containing a dollar sign' => [ + 'import jQuery$1 from "/npm/jquery@3.7.0/+esm";', + [ + ['jquery', '3.7.0'], + ], + ]; } private static function createRemoteEntry(string $importName, string $version, ImportMapType $type = ImportMapType::JS, ?string $packageSpecifier = null): ImportMapEntry diff --git a/src/Symfony/Component/Cache/Tests/Traits/RedisProxiesTest.php b/src/Symfony/Component/Cache/Tests/Traits/RedisProxiesTest.php index 4b144237ecd4b..1e37a44d2656e 100644 --- a/src/Symfony/Component/Cache/Tests/Traits/RedisProxiesTest.php +++ b/src/Symfony/Component/Cache/Tests/Traits/RedisProxiesTest.php @@ -29,15 +29,34 @@ public function testRedisProxy($class) { $version = version_compare(phpversion('redis'), '6', '>') ? '6' : '5'; $proxy = file_get_contents(\dirname(__DIR__, 2)."/Traits/{$class}{$version}Proxy.php"); + $proxy = substr($proxy, 0, 4 + strpos($proxy, '[];')); $expected = substr($proxy, 0, 4 + strpos($proxy, '[];')); $methods = []; + foreach ((new \ReflectionClass(sprintf('Symfony\Component\Cache\Traits\\%s%dProxy', $class, $version)))->getMethods() as $method) { + if ('reset' === $method->name || method_exists(LazyProxyTrait::class, $method->name)) { + continue; + } + $return = $method->getReturnType() instanceof \ReflectionNamedType && 'void' === (string) $method->getReturnType() ? '' : 'return '; + $methods[$method->name] = "\n ".ProxyHelper::exportSignature($method, false, $args)."\n".<<lazyObjectState->realInstance ??= (\$this->lazyObjectState->initializer)())->{$method->name}({$args}); + } + + EOPHP; + } + + uksort($methods, 'strnatcmp'); + $proxy .= implode('', $methods)."}\n"; + + $methods = []; + foreach ((new \ReflectionClass($class))->getMethods() as $method) { if ('reset' === $method->name || method_exists(LazyProxyTrait::class, $method->name)) { continue; } $return = $method->getReturnType() instanceof \ReflectionNamedType && 'void' === (string) $method->getReturnType() ? '' : 'return '; - $methods[] = "\n ".ProxyHelper::exportSignature($method, false, $args)."\n".<<name] = "\n ".ProxyHelper::exportSignature($method, false, $args)."\n".<<lazyObjectState->realInstance ??= (\$this->lazyObjectState->initializer)())->{$method->name}({$args}); } diff --git a/src/Symfony/Component/Cache/Traits/Redis6Proxy.php b/src/Symfony/Component/Cache/Traits/Redis6Proxy.php index 0680404fc1eee..c841d4269b30b 100644 --- a/src/Symfony/Component/Cache/Traits/Redis6Proxy.php +++ b/src/Symfony/Component/Cache/Traits/Redis6Proxy.php @@ -25,6 +25,7 @@ class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class); */ class Redis6Proxy extends \Redis implements ResetInterface, LazyObjectInterface { + use Redis6ProxyTrait; use LazyProxyTrait { resetLazyObject as reset; } @@ -226,11 +227,6 @@ public function discard(): \Redis|bool return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->discard(...\func_get_args()); } - public function dump($key): \Redis|string - { - return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->dump(...\func_get_args()); - } - public function echo($str): \Redis|false|string { return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->echo(...\func_get_args()); @@ -511,16 +507,6 @@ public function hMset($key, $fieldvals): \Redis|bool return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hMset(...\func_get_args()); } - public function hRandField($key, $options = null): \Redis|array|string - { - return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hRandField(...\func_get_args()); - } - - public function hSet($key, $member, $value): \Redis|false|int - { - return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hSet(...\func_get_args()); - } - public function hSetNx($key, $field, $value): \Redis|bool { return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hSetNx(...\func_get_args()); @@ -651,11 +637,6 @@ public function ltrim($key, $start, $end): \Redis|bool return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->ltrim(...\func_get_args()); } - public function mget($keys): \Redis|array - { - return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->mget(...\func_get_args()); - } - public function migrate($host, $port, $key, $dstdb, $timeout, $copy = false, $replace = false, #[\SensitiveParameter] $credentials = null): \Redis|bool { return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->migrate(...\func_get_args()); @@ -866,11 +847,6 @@ public function sPop($key, $count = 0): \Redis|array|false|string return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->sPop(...\func_get_args()); } - public function sRandMember($key, $count = 0): \Redis|array|false|string - { - return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->sRandMember(...\func_get_args()); - } - public function sUnion($key, ...$other_keys): \Redis|array|false { return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->sUnion(...\func_get_args()); diff --git a/src/Symfony/Component/Cache/Traits/Redis6ProxyTrait.php b/src/Symfony/Component/Cache/Traits/Redis6ProxyTrait.php new file mode 100644 index 0000000000000..d339e560c7cd3 --- /dev/null +++ b/src/Symfony/Component/Cache/Traits/Redis6ProxyTrait.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +if (version_compare(phpversion('redis'), '6.1.0', '>=')) { + /** + * @internal + */ + trait Redis6ProxyTrait + { + public function dump($key): \Redis|string|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->dump(...\func_get_args()); + } + + public function hRandField($key, $options = null): \Redis|array|string|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hRandField(...\func_get_args()); + } + + public function hSet($key, $fields_and_vals): \Redis|false|int + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hSet(...\func_get_args()); + } + + public function mget($keys): \Redis|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->mget(...\func_get_args()); + } + + public function sRandMember($key, $count = 0): mixed + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->sRandMember(...\func_get_args()); + } + + public function waitaof($numlocal, $numreplicas, $timeout): \Redis|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->waitaof(...\func_get_args()); + } + } +} else { + /** + * @internal + */ + trait Redis6ProxyTrait + { + public function dump($key): \Redis|string + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->dump(...\func_get_args()); + } + + public function hRandField($key, $options = null): \Redis|array|string + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hRandField(...\func_get_args()); + } + + public function hSet($key, $member, $value): \Redis|false|int + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hSet(...\func_get_args()); + } + + public function mget($keys): \Redis|array + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->mget(...\func_get_args()); + } + + public function sRandMember($key, $count = 0): \Redis|array|false|string + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->sRandMember(...\func_get_args()); + } + } +} diff --git a/src/Symfony/Component/Cache/Traits/RedisCluster6Proxy.php b/src/Symfony/Component/Cache/Traits/RedisCluster6Proxy.php index fafc4acf2df06..c19aa1620a636 100644 --- a/src/Symfony/Component/Cache/Traits/RedisCluster6Proxy.php +++ b/src/Symfony/Component/Cache/Traits/RedisCluster6Proxy.php @@ -25,6 +25,7 @@ class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class); */ class RedisCluster6Proxy extends \RedisCluster implements ResetInterface, LazyObjectInterface { + use RedisCluster6ProxyTrait; use LazyProxyTrait { resetLazyObject as reset; } @@ -656,11 +657,6 @@ public function pttl($key): \RedisCluster|false|int return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->pttl(...\func_get_args()); } - public function publish($channel, $message): \RedisCluster|bool - { - return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->publish(...\func_get_args()); - } - public function pubsub($key_or_address, ...$values): mixed { return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->pubsub(...\func_get_args()); diff --git a/src/Symfony/Component/Cache/Traits/RedisCluster6ProxyTrait.php b/src/Symfony/Component/Cache/Traits/RedisCluster6ProxyTrait.php new file mode 100644 index 0000000000000..7addffb97c454 --- /dev/null +++ b/src/Symfony/Component/Cache/Traits/RedisCluster6ProxyTrait.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +if (version_compare(phpversion('redis'), '6.1.0', '>')) { + /** + * @internal + */ + trait RedisCluster6ProxyTrait + { + public function getex($key, $options = []): \RedisCluster|string|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->getex(...\func_get_args()); + } + + public function publish($channel, $message): \RedisCluster|bool|int + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->publish(...\func_get_args()); + } + + public function waitaof($key_or_address, $numlocal, $numreplicas, $timeout): \RedisCluster|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->waitaof(...\func_get_args()); + } + } +} else { + /** + * @internal + */ + trait RedisCluster6ProxyTrait + { + public function publish($channel, $message): \RedisCluster|bool + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->publish(...\func_get_args()); + } + } +} diff --git a/src/Symfony/Component/Console/Output/StreamOutput.php b/src/Symfony/Component/Console/Output/StreamOutput.php index ca90096a440ed..f51d0376336fb 100644 --- a/src/Symfony/Component/Console/Output/StreamOutput.php +++ b/src/Symfony/Component/Console/Output/StreamOutput.php @@ -93,7 +93,7 @@ protected function doWrite(string $message, bool $newline) protected function hasColorSupport(): bool { // Follow https://no-color.org/ - if ('' !== ($_SERVER['NO_COLOR'] ?? getenv('NO_COLOR') ?: '')) { + if ('' !== (($_SERVER['NO_COLOR'] ?? getenv('NO_COLOR'))[0] ?? '')) { return false; } diff --git a/src/Symfony/Component/Console/Question/ChoiceQuestion.php b/src/Symfony/Component/Console/Question/ChoiceQuestion.php index e449ff683d20a..465f3184fb97f 100644 --- a/src/Symfony/Component/Console/Question/ChoiceQuestion.php +++ b/src/Symfony/Component/Console/Question/ChoiceQuestion.php @@ -26,11 +26,11 @@ class ChoiceQuestion extends Question private string $errorMessage = 'Value "%s" is invalid'; /** - * @param string $question The question to ask to the user - * @param array $choices The list of available choices - * @param mixed $default The default answer to return + * @param string $question The question to ask to the user + * @param array $choices The list of available choices + * @param string|bool|int|float|null $default The default answer to return */ - public function __construct(string $question, array $choices, mixed $default = null) + public function __construct(string $question, array $choices, string|bool|int|float|null $default = null) { if (!$choices) { throw new \LogicException('Choice question must have at least 1 choice available.'); diff --git a/src/Symfony/Component/Console/Resources/completion.bash b/src/Symfony/Component/Console/Resources/completion.bash index 0d76eacc3b748..64c6a338fcc1b 100644 --- a/src/Symfony/Component/Console/Resources/completion.bash +++ b/src/Symfony/Component/Console/Resources/completion.bash @@ -17,7 +17,7 @@ _sf_{{ COMMAND_NAME }}() { done # Use newline as only separator to allow space in completion values - IFS=$'\n' + local IFS=$'\n' local sf_cmd="${COMP_WORDS[0]}" # for an alias, get the real script behind it diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php b/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php index 42410cebefdd7..5d2110bf9ff3a 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php @@ -85,7 +85,8 @@ private function findAndSortTaggedServices(string|TaggedIteratorArgument $tagNam } elseif (null === $defaultIndex && $defaultPriorityMethod && $class) { $defaultIndex = PriorityTaggedServiceUtil::getDefault($container, $serviceId, $class, $defaultIndexMethod ?? 'getDefaultName', $tagName, $indexAttribute, $checkTaggedItem); } - $index ??= $defaultIndex ??= $serviceId; + $decorated = $definition->getTag('container.decorator')[0]['id'] ?? null; + $index = $index ?? $defaultIndex ?? $defaultIndex = $decorated ?? $serviceId; $services[] = [$priority, ++$i, $index, $serviceId, $class]; } diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 9e26bf887c3df..62b41a06c4cc2 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -117,7 +117,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface private array $vendors; /** - * @var string[] the list of paths in vendor directories + * @var array the cache for paths being in vendor directories */ private array $pathsInVendor = []; diff --git a/src/Symfony/Component/DependencyInjection/ExpressionLanguageProvider.php b/src/Symfony/Component/DependencyInjection/ExpressionLanguageProvider.php index 6ae797d864ecc..60479ea37bd82 100644 --- a/src/Symfony/Component/DependencyInjection/ExpressionLanguageProvider.php +++ b/src/Symfony/Component/DependencyInjection/ExpressionLanguageProvider.php @@ -45,7 +45,7 @@ public function getFunctions(): array new ExpressionFunction('env', fn ($arg) => sprintf('$container->getEnv(%s)', $arg), function (array $variables, $value) { if (!$this->getEnv) { - throw new LogicException('You need to pass a getEnv closure to the expression langage provider to use the "env" function.'); + throw new LogicException('You need to pass a getEnv closure to the expression language provider to use the "env" function.'); } return ($this->getEnv)($value); diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 147a7db115efa..2bdedef93f11d 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -458,7 +458,33 @@ private function parseFileToDOM(string $file): \DOMDocument try { $dom = XmlUtils::loadFile($file, $this->validateSchema(...)); } catch (\InvalidArgumentException $e) { - throw new InvalidArgumentException(sprintf('Unable to parse file "%s": ', $file).$e->getMessage(), $e->getCode(), $e); + $invalidSecurityElements = []; + $errors = explode("\n", $e->getMessage()); + foreach ($errors as $i => $error) { + if (preg_match("#^\[ERROR 1871] Element '\{http://symfony\.com/schema/dic/security}([^']+)'#", $error, $matches)) { + $invalidSecurityElements[$i] = $matches[1]; + } + } + if ($invalidSecurityElements) { + $dom = XmlUtils::loadFile($file); + + foreach ($invalidSecurityElements as $errorIndex => $tagName) { + foreach ($dom->getElementsByTagNameNS('http://symfony.com/schema/dic/security', $tagName) as $element) { + if (!$parent = $element->parentNode) { + continue; + } + if ('http://symfony.com/schema/dic/security' !== $parent->namespaceURI) { + continue; + } + if ('provider' === $parent->localName || 'firewall' === $parent->localName) { + unset($errors[$errorIndex]); + } + } + } + } + if ($errors) { + throw new InvalidArgumentException(sprintf('Unable to parse file "%s": ', $file).implode("/n", $errors), $e->getCode(), $e); + } } $this->validateExtensions($dom, $file); @@ -858,6 +884,6 @@ private function loadFromExtensions(\DOMDocument $xml): void */ public static function convertDomElementToArray(\DOMElement $element): mixed { - return XmlUtils::convertDomElementToArray($element); + return XmlUtils::convertDomElementToArray($element, false); } } diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index 42645825d54cb..ae5a625df6349 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -450,8 +450,9 @@ private function parseDefinition(string $id, array|string|null $service, string return $return ? $alias : $this->container->setAlias($id, $alias); } + $changes = []; if (null !== $definition) { - // no-op + $changes = $definition->getChanges(); } elseif ($this->isLoadingInstanceof) { $definition = new ChildDefinition(''); } elseif (isset($service['parent'])) { @@ -474,7 +475,7 @@ private function parseDefinition(string $id, array|string|null $service, string $definition->setAutoconfigured($defaults['autoconfigure']); } - $definition->setChanges([]); + $definition->setChanges($changes); if (isset($service['class'])) { $definition->setClass($service['class']); @@ -556,7 +557,7 @@ private function parseDefinition(string $id, array|string|null $service, string } if (\is_string($k)) { - throw new InvalidArgumentException(sprintf('Invalid method call for service "%s", did you forgot a leading dash before "%s: ..." in "%s"?', $id, $k, $file)); + throw new InvalidArgumentException(sprintf('Invalid method call for service "%s", did you forget a leading dash before "%s: ..." in "%s"?', $id, $k, $file)); } if (isset($call['method']) && \is_string($call['method'])) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/PriorityTaggedServiceTraitTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/PriorityTaggedServiceTraitTest.php index bc7387ab4de44..acb3e13690351 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/PriorityTaggedServiceTraitTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/PriorityTaggedServiceTraitTest.php @@ -153,6 +153,9 @@ public function testTheIndexedTagsByDefaultIndexMethod() $container->register('service4', HelloInterface::class)->addTag('my_custom_tag'); + $definition = $container->register('debug.service5', \stdClass::class)->addTag('my_custom_tag'); + $definition->addTag('container.decorator', ['id' => 'service5']); + $priorityTaggedServiceTraitImplementation = new PriorityTaggedServiceTraitImplementation(); $tag = new TaggedIteratorArgument('my_custom_tag', 'foo', 'getFooBar'); @@ -161,6 +164,7 @@ public function testTheIndexedTagsByDefaultIndexMethod() 'service1' => new TypedReference('service1', FooTagClass::class), '10' => new TypedReference('service3', IntTagClass::class), 'service4' => new TypedReference('service4', HelloInterface::class), + 'service5' => new TypedReference('debug.service5', \stdClass::class), ]; $services = $priorityTaggedServiceTraitImplementation->test($tag, $container); $this->assertSame(array_keys($expected), array_keys($services)); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterAutoconfigureAttributesPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterAutoconfigureAttributesPassTest.php index 877c50f027fa2..7328aa10cb7bb 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterAutoconfigureAttributesPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterAutoconfigureAttributesPassTest.php @@ -19,6 +19,12 @@ use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureAttributed; use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfiguredInterface; +use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureRepeated; +use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureRepeatedBindings; +use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureRepeatedCalls; +use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureRepeatedOverwrite; +use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureRepeatedProperties; +use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfigureRepeatedTag; use Symfony\Component\DependencyInjection\Tests\Fixtures\ParentNotExists; use Symfony\Component\DependencyInjection\Tests\Fixtures\StaticConstructorAutoconfigure; @@ -76,6 +82,99 @@ public function testAutoconfiguredTag() $this->assertEquals([AutoconfiguredInterface::class => $expected], $container->getAutoconfiguredInstanceof()); } + public function testAutoconfiguredRepeated() + { + $container = new ContainerBuilder(); + $container->register('foo', AutoconfigureRepeated::class) + ->setAutoconfigured(true); + + (new RegisterAutoconfigureAttributesPass())->process($container); + + $expected = (new ChildDefinition('')) + ->setLazy(true) + ->setPublic(true) + ->setShared(false); + + $this->assertEquals([AutoconfigureRepeated::class => $expected], $container->getAutoconfiguredInstanceof()); + } + + public function testAutoconfiguredRepeatedOverwrite() + { + $container = new ContainerBuilder(); + $container->register('foo', AutoconfigureRepeatedOverwrite::class) + ->setAutoconfigured(true); + + (new RegisterAutoconfigureAttributesPass())->process($container); + + $expected = (new ChildDefinition('')) + ->setLazy(true) + ->setPublic(false) + ->setShared(true); + + $this->assertEquals([AutoconfigureRepeatedOverwrite::class => $expected], $container->getAutoconfiguredInstanceof()); + } + + public function testAutoconfiguredRepeatedTag() + { + $container = new ContainerBuilder(); + $container->register('foo', AutoconfigureRepeatedTag::class) + ->setAutoconfigured(true); + + (new RegisterAutoconfigureAttributesPass())->process($container); + + $expected = (new ChildDefinition('')) + ->addTag('foo', ['priority' => 2]) + ->addTag('bar'); + + $this->assertEquals([AutoconfigureRepeatedTag::class => $expected], $container->getAutoconfiguredInstanceof()); + } + + public function testAutoconfiguredRepeatedCalls() + { + $container = new ContainerBuilder(); + $container->register('foo', AutoconfigureRepeatedCalls::class) + ->setAutoconfigured(true); + + (new RegisterAutoconfigureAttributesPass())->process($container); + + $expected = (new ChildDefinition('')) + ->addMethodCall('setBar', ['arg2']) + ->addMethodCall('setFoo', ['arg1']); + + $this->assertEquals([AutoconfigureRepeatedCalls::class => $expected], $container->getAutoconfiguredInstanceof()); + } + + public function testAutoconfiguredRepeatedBindingsOverwrite() + { + $container = new ContainerBuilder(); + $container->register('foo', AutoconfigureRepeatedBindings::class) + ->setAutoconfigured(true); + + (new RegisterAutoconfigureAttributesPass())->process($container); + + $expected = (new ChildDefinition('')) + ->setBindings(['$arg' => new BoundArgument('bar', false, BoundArgument::INSTANCEOF_BINDING, realpath(__DIR__.'/../Fixtures/AutoconfigureRepeatedBindings.php'))]); + + $this->assertEquals([AutoconfigureRepeatedBindings::class => $expected], $container->getAutoconfiguredInstanceof()); + } + + public function testAutoconfiguredRepeatedPropertiesOverwrite() + { + $container = new ContainerBuilder(); + $container->register('foo', AutoconfigureRepeatedProperties::class) + ->setAutoconfigured(true); + + (new RegisterAutoconfigureAttributesPass())->process($container); + + $expected = (new ChildDefinition('')) + ->setProperties([ + '$foo' => 'bar', + '$bar' => 'baz', + ]); + + $this->assertEquals([AutoconfigureRepeatedProperties::class => $expected], $container->getAutoconfiguredInstanceof()); + } + public function testMissingParent() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/AutoconfigureRepeated.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/AutoconfigureRepeated.php new file mode 100644 index 0000000000000..1b6bc639d1b10 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/AutoconfigureRepeated.php @@ -0,0 +1,11 @@ + 'foo'])] +#[Autoconfigure(bind: ['$arg' => 'bar'])] +class AutoconfigureRepeatedBindings +{ +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/AutoconfigureRepeatedCalls.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/AutoconfigureRepeatedCalls.php new file mode 100644 index 0000000000000..ba794a705e000 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/AutoconfigureRepeatedCalls.php @@ -0,0 +1,18 @@ + 'to be replaced', '$bar' => 'existing to be replaced'])] +#[Autoconfigure(properties: ['$foo' => 'bar', '$bar' => 'baz'])] +class AutoconfigureRepeatedProperties +{ +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/AutoconfigureRepeatedTag.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/AutoconfigureRepeatedTag.php new file mode 100644 index 0000000000000..671bc6074541a --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/AutoconfigureRepeatedTag.php @@ -0,0 +1,11 @@ + 2])] +#[AutoconfigureTag('bar')] +class AutoconfigureRepeatedTag +{ +} diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php index 93dbeb69bc612..af53599f37d2b 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php @@ -460,4 +460,11 @@ function (ExpressionLanguage $el) { ], ]; } + + public function testParseAlreadyParsedExpressionReturnsSameObject() + { + $el = new ExpressionLanguage(); + $parsed = $el->parse('1 + 1', []); + $this->assertSame($parsed, $el->parse($parsed, [])); + } } diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/Node/BinaryNodeTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/Node/BinaryNodeTest.php index a188b3e066e7a..fd6cc53868b8b 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/Node/BinaryNodeTest.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/Node/BinaryNodeTest.php @@ -218,6 +218,26 @@ public function testCompileMatchesWithInvalidRegexpAsExpression() eval('$regexp = "this is not a regexp"; '.$compiler->getSource().';'); } + public function testDivisionByZero() + { + $node = new BinaryNode('/', new ConstantNode(1), new ConstantNode(0)); + + $this->expectException(\DivisionByZeroError::class); + $this->expectExceptionMessage('Division by zero.'); + + $node->evaluate([], []); + } + + public function testModuloByZero() + { + $node = new BinaryNode('%', new ConstantNode(1), new ConstantNode(0)); + + $this->expectException(\DivisionByZeroError::class); + $this->expectExceptionMessage('Modulo by zero.'); + + $node->evaluate([], []); + } + /** * @group legacy */ diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/Node/NodeTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/Node/NodeTest.php index 158973cec3aa5..44f8bd7be5581 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/Node/NodeTest.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/Node/NodeTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\ExpressionLanguage\Tests\Node; use PHPUnit\Framework\TestCase; +use Symfony\Component\ExpressionLanguage\Compiler; use Symfony\Component\ExpressionLanguage\Node\ConstantNode; use Symfony\Component\ExpressionLanguage\Node\Node; @@ -38,4 +39,33 @@ public function testSerialization() $this->assertEquals($node, $unserializedNode); } + + public function testCompileActuallyCompilesAllNodes() + { + $nodes = []; + foreach (range(1, 10) as $ignored) { + $node = $this->createMock(Node::class); + $node->expects($this->once())->method('compile'); + + $nodes[] = $node; + } + + $node = new Node($nodes); + $node->compile($this->createMock(Compiler::class)); + } + + public function testEvaluateActuallyEvaluatesAllNodes() + { + $nodes = []; + foreach (range(1, 3) as $i) { + $node = $this->createMock(Node::class); + $node->expects($this->once())->method('evaluate') + ->willReturn($i); + + $nodes[] = $node; + } + + $node = new Node($nodes); + $this->assertSame([1, 2, 3], $node->evaluate([], [])); + } } diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/ParserTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/ParserTest.php index 9c546e394a451..1429be8cf5b4e 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/ParserTest.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/ParserTest.php @@ -37,6 +37,17 @@ public function testParseWithZeroInNames() $parser->parse($lexer->tokenize('foo'), [0]); } + public function testParsePrimaryExpressionWithUnknownFunctionThrows() + { + $parser = new Parser([]); + $stream = (new Lexer())->tokenize('foo()'); + + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('The function "foo" does not exist around position 1 for expression `foo()`.'); + + $parser->parse($stream); + } + /** * @dataProvider getParseData */ diff --git a/src/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php b/src/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php index 34cced6889b1b..f5fd2d4dc5b0c 100644 --- a/src/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php +++ b/src/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php @@ -63,8 +63,9 @@ public function current(): SplFileInfo $subPathname .= $this->directorySeparator; } $subPathname .= $this->getFilename(); + $basePath = $this->rootPath; - if ('/' !== $basePath = $this->rootPath) { + if ('/' !== $basePath && !str_ends_with($basePath, $this->directorySeparator) && !str_ends_with($basePath, '/')) { $basePath .= $this->directorySeparator; } diff --git a/src/Symfony/Component/Finder/Tests/Iterator/RecursiveDirectoryIteratorTest.php b/src/Symfony/Component/Finder/Tests/Iterator/RecursiveDirectoryIteratorTest.php index 49144505f7883..c63dd6e734c35 100644 --- a/src/Symfony/Component/Finder/Tests/Iterator/RecursiveDirectoryIteratorTest.php +++ b/src/Symfony/Component/Finder/Tests/Iterator/RecursiveDirectoryIteratorTest.php @@ -24,26 +24,38 @@ protected function setUp(): void /** * @group network + * @group integration */ public function testRewindOnFtp() { - $i = new RecursiveDirectoryIterator('ftp://test.rebex.net/', \RecursiveDirectoryIterator::SKIP_DOTS); + if (!getenv('INTEGRATION_FTP_URL')) { + self::markTestSkipped('INTEGRATION_FTP_URL env var is not defined.'); + } + + $i = new RecursiveDirectoryIterator(getenv('INTEGRATION_FTP_URL').\DIRECTORY_SEPARATOR, \RecursiveDirectoryIterator::SKIP_DOTS); $i->rewind(); - $this->assertTrue(true); + $this->expectNotToPerformAssertions(); } /** * @group network + * @group integration */ public function testSeekOnFtp() { - $i = new RecursiveDirectoryIterator('ftp://test.rebex.net/', \RecursiveDirectoryIterator::SKIP_DOTS); + if (!getenv('INTEGRATION_FTP_URL')) { + self::markTestSkipped('INTEGRATION_FTP_URL env var is not defined.'); + } + + $ftpUrl = getenv('INTEGRATION_FTP_URL'); + + $i = new RecursiveDirectoryIterator($ftpUrl.\DIRECTORY_SEPARATOR, \RecursiveDirectoryIterator::SKIP_DOTS); $contains = [ - 'ftp://test.rebex.net'.\DIRECTORY_SEPARATOR.'pub', - 'ftp://test.rebex.net'.\DIRECTORY_SEPARATOR.'readme.txt', + $ftpUrl.\DIRECTORY_SEPARATOR.'pub', + $ftpUrl.\DIRECTORY_SEPARATOR.'readme.txt', ]; $actual = []; @@ -55,4 +67,31 @@ public function testSeekOnFtp() $this->assertEquals($contains, $actual); } + + public function testTrailingDirectorySeparatorIsStripped() + { + $fixturesDirectory = __DIR__ . '/../Fixtures/'; + $actual = []; + + foreach (new RecursiveDirectoryIterator($fixturesDirectory, RecursiveDirectoryIterator::SKIP_DOTS) as $file) { + $actual[] = $file->getPathname(); + } + + sort($actual); + + $expected = [ + $fixturesDirectory.'.dot', + $fixturesDirectory.'A', + $fixturesDirectory.'copy', + $fixturesDirectory.'dolor.txt', + $fixturesDirectory.'gitignore', + $fixturesDirectory.'ipsum.txt', + $fixturesDirectory.'lorem.txt', + $fixturesDirectory.'one', + $fixturesDirectory.'r+e.gex[c]a(r)s', + $fixturesDirectory.'with space', + ]; + + $this->assertEquals($expected, $actual); + } } diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php index 7ddcb20079c9e..d083d2cb527e4 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php @@ -106,7 +106,8 @@ public function reverseTransform(mixed $value): int|float|null $value = str_replace(',', $decSep, $value); } - if (str_contains($value, $decSep)) { + //If the value is in exponential notation with a negative exponent, we end up with a float value too + if (str_contains($value, $decSep) || false !== stripos($value, 'e-')) { $type = \NumberFormatter::TYPE_DOUBLE; } else { $type = \PHP_INT_SIZE === 8 diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/ValueToDuplicatesTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/ValueToDuplicatesTransformer.php index 2399abf73c7a3..083397bb46773 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/ValueToDuplicatesTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/ValueToDuplicatesTransformer.php @@ -58,7 +58,7 @@ public function reverseTransform(mixed $array): mixed $emptyKeys = []; foreach ($this->keys as $key) { - if (isset($array[$key]) && '' !== $array[$key] && false !== $array[$key] && [] !== $array[$key]) { + if (isset($array[$key]) && false !== $array[$key] && [] !== $array[$key]) { if ($array[$key] !== $result) { throw new TransformationFailedException('All values in the array should be the same.'); } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php index a1dc724fd7aec..f3149b79edf2f 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php @@ -632,4 +632,33 @@ public function testReverseTransformSmallInt() $this->assertSame(1.0, $transformer->reverseTransform('1')); } + + /** + * @dataProvider eNotationProvider + */ + public function testReverseTransformENotation($output, $input) + { + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('en'); + + $transformer = new NumberToLocalizedStringTransformer(); + + $this->assertSame($output, $transformer->reverseTransform($input)); + } + + public static function eNotationProvider(): array + { + return [ + [0.001, '1E-3'], + [0.001, '1.0E-3'], + [0.001, '1e-3'], + [0.001, '1.0e-03'], + [1000.0, '1E3'], + [1000.0, '1.0E3'], + [1000.0, '1e3'], + [1000.0, '1.0e3'], + [1232.0, '1.232e3'], + ]; + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ValueToDuplicatesTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ValueToDuplicatesTransformerTest.php index 5909a51ef4741..358f21af2e8d1 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ValueToDuplicatesTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ValueToDuplicatesTransformerTest.php @@ -65,7 +65,7 @@ public function testReverseTransformCompletelyEmpty() 'c' => '', ]; - $this->assertNull($this->transformer->reverseTransform($input)); + $this->assertSame('', $this->transformer->reverseTransform($input)); } public function testReverseTransformCompletelyNull() diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php index 06b9151fbe7a8..e328d37249747 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; +use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Form; use Symfony\Component\Form\Tests\Fixtures\NotMappedType; use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; @@ -188,6 +189,36 @@ public function testSetOptionsPerChildAndOverwrite() $this->assertTrue($form['second']->isRequired()); } + /** + * @dataProvider emptyDataProvider + */ + public function testSubmitNullForTextTypeWithEmptyDataOptionSetToEmptyString($emptyData, $submittedData, $expected) + { + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'type' => TextType::class, + 'options' => [ + 'empty_data' => $emptyData, + ] + ]); + $form->submit($submittedData); + + $this->assertSame($expected, $form->getData()); + } + + public static function emptyDataProvider() + { + yield ['', null, '']; + yield ['', ['first' => null, 'second' => null], '']; + yield ['', ['first' => '', 'second' => null], '']; + yield ['', ['first' => null, 'second' => ''], '']; + yield ['', ['first' => '', 'second' => ''], '']; + yield [null, null, null]; + yield [null, ['first' => null, 'second' => null], null]; + yield [null, ['first' => '', 'second' => null], null]; + yield [null, ['first' => null, 'second' => ''], null]; + yield [null, ['first' => '', 'second' => ''], null]; + } + public function testSubmitUnequal() { $input = ['first' => 'foo', 'second' => 'bar']; diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php index 4446a031e9695..419aa890c3730 100644 --- a/src/Symfony/Component/HttpClient/CurlHttpClient.php +++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php @@ -66,7 +66,7 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface, * * @see HttpClientInterface::OPTIONS_DEFAULTS for available options */ - public function __construct(array $defaultOptions = [], int $maxHostConnections = 6, int $maxPendingPushes = 50) + public function __construct(array $defaultOptions = [], int $maxHostConnections = 6, int $maxPendingPushes = 0) { if (!\extension_loaded('curl')) { throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\CurlHttpClient" as the "curl" extension is not installed.'); @@ -188,10 +188,10 @@ public function request(string $method, string $url, array $options = []): Respo $multi->reset(); } - foreach ($options['resolve'] as $host => $ip) { - $resolve[] = null === $ip ? "-$host:$port" : "$host:$port:$ip"; - $multi->dnsCache->hostnames[$host] = $ip; - $multi->dnsCache->removals["-$host:$port"] = "-$host:$port"; + foreach ($options['resolve'] as $resolveHost => $ip) { + $resolve[] = null === $ip ? "-$resolveHost:$port" : "$resolveHost:$port:$ip"; + $multi->dnsCache->hostnames[$resolveHost] = $ip; + $multi->dnsCache->removals["-$resolveHost:$port"] = "-$resolveHost:$port"; } $curlopts[\CURLOPT_RESOLVE] = $resolve; diff --git a/src/Symfony/Component/HttpClient/HttpClientTrait.php b/src/Symfony/Component/HttpClient/HttpClientTrait.php index b83baf1f4c1cb..393da774c566a 100644 --- a/src/Symfony/Component/HttpClient/HttpClientTrait.php +++ b/src/Symfony/Component/HttpClient/HttpClientTrait.php @@ -557,6 +557,8 @@ private static function jsonEncode(mixed $value, ?int $flags = null, int $maxDep */ private static function resolveUrl(array $url, ?array $base, array $queryDefaults = []): array { + $givenUrl = $url; + if (null !== $base && '' === ($base['scheme'] ?? '').($base['authority'] ?? '')) { throw new InvalidArgumentException(sprintf('Invalid "base_uri" option: host or scheme is missing in "%s".', implode('', $base))); } @@ -610,6 +612,10 @@ private static function resolveUrl(array $url, ?array $base, array $queryDefault $url['query'] = null; } + if (null !== $url['scheme'] && null === $url['authority']) { + throw new InvalidArgumentException(\sprintf('Invalid URL: host is missing in "%s".', implode('', $givenUrl))); + } + return $url; } diff --git a/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php index 1c3076324da02..03a939b559e1f 100644 --- a/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php +++ b/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php @@ -22,13 +22,15 @@ class CurlHttpClientTest extends HttpClientTestCase { protected function getHttpClient(string $testCase): HttpClientInterface { - if (str_contains($testCase, 'Push')) { - if (!\defined('CURLMOPT_PUSHFUNCTION') || 0x073D00 > ($v = curl_version())['version_number'] || !(\CURL_VERSION_HTTP2 & $v['features'])) { - $this->markTestSkipped('curl <7.61 is used or it is not compiled with support for HTTP/2 PUSH'); - } + if (!str_contains($testCase, 'Push')) { + return new CurlHttpClient(['verify_peer' => false, 'verify_host' => false]); } - return new CurlHttpClient(['verify_peer' => false, 'verify_host' => false]); + if (!\defined('CURLMOPT_PUSHFUNCTION') || 0x073D00 > ($v = curl_version())['version_number'] || !(\CURL_VERSION_HTTP2 & $v['features'])) { + $this->markTestSkipped('curl <7.61 is used or it is not compiled with support for HTTP/2 PUSH'); + } + + return new CurlHttpClient(['verify_peer' => false, 'verify_host' => false], 6, 50); } public function testBindToPort() @@ -120,4 +122,20 @@ public function testOverridingInternalAttributesUsingCurlOptions() ], ]); } + + public function testKeepAuthorizationHeaderOnRedirectToSameHostWithConfiguredHostToIpAddressMapping() + { + $httpClient = $this->getHttpClient(__FUNCTION__); + $response = $httpClient->request('POST', 'http://127.0.0.1:8057/301', [ + 'headers' => [ + 'Authorization' => 'Basic Zm9vOmJhcg==', + ], + 'resolve' => [ + 'symfony.com' => '10.10.10.10', + ], + ]); + + $this->assertSame(200, $response->getStatusCode()); + $this->assertSame('/302', $response->toArray()['REQUEST_URI'] ?? null); + } } diff --git a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php index b7eac0f82a2aa..f022bc5a44eee 100644 --- a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php +++ b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php @@ -12,6 +12,7 @@ namespace Symfony\Component\HttpClient\Tests; use Symfony\Component\HttpClient\Exception\ClientException; +use Symfony\Component\HttpClient\Exception\InvalidArgumentException; use Symfony\Component\HttpClient\Exception\TransportException; use Symfony\Component\HttpClient\Internal\ClientState; use Symfony\Component\HttpClient\Response\StreamWrapper; @@ -451,6 +452,16 @@ public function testNullBody() $this->expectNotToPerformAssertions(); } + public function testMisspelledScheme() + { + $httpClient = $this->getHttpClient(__FUNCTION__); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid URL: host is missing in "http:/localhost:8057/".'); + + $httpClient->request('GET', 'http:/localhost:8057/'); + } + /** * @dataProvider getRedirectWithAuthTests */ diff --git a/src/Symfony/Component/HttpClient/Tests/HttpClientTraitTest.php b/src/Symfony/Component/HttpClient/Tests/HttpClientTraitTest.php index 3e81f429622a0..c9fa7c791f336 100644 --- a/src/Symfony/Component/HttpClient/Tests/HttpClientTraitTest.php +++ b/src/Symfony/Component/HttpClient/Tests/HttpClientTraitTest.php @@ -175,7 +175,6 @@ public function testResolveUrl(string $base, string $url, string $expected) public static function provideResolveUrl(): array { return [ - [self::RFC3986_BASE, 'http:h', 'http:h'], [self::RFC3986_BASE, 'g', 'http://a/b/c/g'], [self::RFC3986_BASE, './g', 'http://a/b/c/g'], [self::RFC3986_BASE, 'g/', 'http://a/b/c/g/'], @@ -229,7 +228,6 @@ public static function provideResolveUrl(): array ['http://u:p@a/b/c/d;p?q', '.', 'http://u:p@a/b/c/'], // path ending with slash or no slash at all ['http://a/b/c/d/', 'e', 'http://a/b/c/d/e'], - ['http:no-slash', 'e', 'http:e'], // falsey relative parts [self::RFC3986_BASE, '//0', 'http://0/'], [self::RFC3986_BASE, '0', 'http://a/b/c/0'], diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php index 184666978cf42..6ffe8890571b6 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php @@ -237,7 +237,9 @@ public function handle(Request $request, int $type = HttpKernelInterface::MAIN_R $response->prepare($request); - $response->isNotModified($request); + if (HttpKernelInterface::MAIN_REQUEST === $type) { + $response->isNotModified($request); + } return $response; } diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 22c3bc2b3372e..8fa757ffe278e 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -76,11 +76,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '6.4.10'; - public const VERSION_ID = 60410; + public const VERSION = '6.4.11'; + public const VERSION_ID = 60411; public const MAJOR_VERSION = 6; public const MINOR_VERSION = 4; - public const RELEASE_VERSION = 10; + public const RELEASE_VERSION = 11; public const EXTRA_VERSION = ''; public const END_OF_MAINTENANCE = '11/2026'; diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php index 1f63686053d51..915af2f35f07f 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php @@ -1329,6 +1329,175 @@ public function testEsiCacheSendsTheLowestTtlForHeadRequests() $this->assertEquals(100, $this->response->getTtl()); } + public function testEsiCacheIncludesEmbeddedResponseContentWhenMainResponseFailsRevalidationAndEmbeddedResponseIsFresh() + { + $this->setNextResponses([ + [ + 'status' => 200, + 'body' => 'main ', + 'headers' => [ + 'Cache-Control' => 's-maxage=0', // goes stale immediately + 'Surrogate-Control' => 'content="ESI/1.0"', + 'Last-Modified' => 'Mon, 12 Aug 2024 10:00:00 +0000', + ], + ], + [ + 'status' => 200, + 'body' => 'embedded', + 'headers' => [ + 'Cache-Control' => 's-maxage=10', // stays fresh + 'Last-Modified' => 'Mon, 12 Aug 2024 10:05:00 +0000', + ] + ], + ]); + + // prime the cache + $this->request('GET', '/', [], [], true); + $this->assertSame(200, $this->response->getStatusCode()); + $this->assertSame('main embedded', $this->response->getContent()); + $this->assertSame('Mon, 12 Aug 2024 10:05:00 +0000', $this->response->getLastModified()->format(\DATE_RFC2822)); // max of both values + + $this->setNextResponses([ + [ + // On the next request, the main response has an updated Last-Modified (main page was modified)... + 'status' => 200, + 'body' => 'main ', + 'headers' => [ + 'Cache-Control' => 's-maxage=0', + 'Surrogate-Control' => 'content="ESI/1.0"', + 'Last-Modified' => 'Mon, 12 Aug 2024 10:10:00 +0000', + ], + ], + // no revalidation request happens for the embedded response, since it is still fresh + ]); + + // Re-request with Last-Modified time that we received when the cache was primed + $this->request('GET', '/', ['HTTP_IF_MODIFIED_SINCE' => 'Mon, 12 Aug 2024 10:05:00 +0000'], [], true); + + $this->assertSame(200, $this->response->getStatusCode()); + + // The cache should use the content ("embedded") from the cached entry + $this->assertSame('main embedded', $this->response->getContent()); + + $traces = $this->cache->getTraces(); + $this->assertSame(['stale', 'invalid', 'store'], $traces['GET /']); + + // The embedded resource was still fresh + $this->assertSame(['fresh'], $traces['GET /foo']); + } + + public function testEsiCacheIncludesEmbeddedResponseContentWhenMainResponseFailsRevalidationAndEmbeddedResponseIsValid() + { + $this->setNextResponses([ + [ + 'status' => 200, + 'body' => 'main ', + 'headers' => [ + 'Cache-Control' => 's-maxage=0', // goes stale immediately + 'Surrogate-Control' => 'content="ESI/1.0"', + 'Last-Modified' => 'Mon, 12 Aug 2024 10:00:00 +0000', + ], + ], + [ + 'status' => 200, + 'body' => 'embedded', + 'headers' => [ + 'Cache-Control' => 's-maxage=0', // goes stale immediately + 'Last-Modified' => 'Mon, 12 Aug 2024 10:05:00 +0000', + ] + ], + ]); + + // prime the cache + $this->request('GET', '/', [], [], true); + $this->assertSame(200, $this->response->getStatusCode()); + $this->assertSame('main embedded', $this->response->getContent()); + $this->assertSame('Mon, 12 Aug 2024 10:05:00 +0000', $this->response->getLastModified()->format(\DATE_RFC2822)); // max of both values + + $this->setNextResponses([ + [ + // On the next request, the main response has an updated Last-Modified (main page was modified)... + 'status' => 200, + 'body' => 'main ', + 'headers' => [ + 'Cache-Control' => 's-maxage=0', + 'Surrogate-Control' => 'content="ESI/1.0"', + 'Last-Modified' => 'Mon, 12 Aug 2024 10:10:00 +0000', + ], + ], + [ + // We have a stale cache entry for the embedded response which will be revalidated. + // Let's assume the resource did not change, so the controller sends a 304 without content body. + 'status' => 304, + 'body' => '', + 'headers' => [ + 'Cache-Control' => 's-maxage=0', + ], + ], + ]); + + // Re-request with Last-Modified time that we received when the cache was primed + $this->request('GET', '/', ['HTTP_IF_MODIFIED_SINCE' => 'Mon, 12 Aug 2024 10:05:00 +0000'], [], true); + + $this->assertSame(200, $this->response->getStatusCode()); + + // The cache should use the content ("embedded") from the cached entry + $this->assertSame('main embedded', $this->response->getContent()); + + $traces = $this->cache->getTraces(); + $this->assertSame(['stale', 'invalid', 'store'], $traces['GET /']); + + // Check that the embedded resource was successfully revalidated + $this->assertSame(['stale', 'valid', 'store'], $traces['GET /foo']); + } + + public function testEsiCacheIncludesEmbeddedResponseContentWhenMainAndEmbeddedResponseAreFresh() + { + $this->setNextResponses([ + [ + 'status' => 200, + 'body' => 'main ', + 'headers' => [ + 'Cache-Control' => 's-maxage=10', + 'Surrogate-Control' => 'content="ESI/1.0"', + 'Last-Modified' => 'Mon, 12 Aug 2024 10:05:00 +0000', + ], + ], + [ + 'status' => 200, + 'body' => 'embedded', + 'headers' => [ + 'Cache-Control' => 's-maxage=10', + 'Last-Modified' => 'Mon, 12 Aug 2024 10:00:00 +0000', + ] + ], + ]); + + // prime the cache + $this->request('GET', '/', [], [], true); + $this->assertSame(200, $this->response->getStatusCode()); + $this->assertSame('main embedded', $this->response->getContent()); + $this->assertSame('Mon, 12 Aug 2024 10:05:00 +0000', $this->response->getLastModified()->format(\DATE_RFC2822)); + + // Assume that a client received 'Mon, 12 Aug 2024 10:00:00 +0000' as last-modified information in the past. This may, for example, + // be the case when the "main" response at that point had an older Last-Modified time, so the embedded response's Last-Modified time + // governed the result for the combined response. In other words, the client received a Last-Modified time that still validates the + // embedded response as of now, but no longer matches the Last-Modified time of the "main" resource. + // Now this client does a revalidation request. + $this->request('GET', '/', ['HTTP_IF_MODIFIED_SINCE' => 'Mon, 12 Aug 2024 10:00:00 +0000'], [], true); + + $this->assertSame(200, $this->response->getStatusCode()); + + // The cache should use the content ("embedded") from the cached entry + $this->assertSame('main embedded', $this->response->getContent()); + + $traces = $this->cache->getTraces(); + $this->assertSame(['fresh'], $traces['GET /']); + + // Check that the embedded resource was successfully revalidated + $this->assertSame(['fresh'], $traces['GET /foo']); + } + public function testEsiCacheForceValidation() { $responses = [ diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php index 377253a086978..663e8484be1ea 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php @@ -138,22 +138,48 @@ public function testLastModifiedIsMergedWithEmbeddedResponse() { $cacheStrategy = new ResponseCacheStrategy(); + $mainResponse = new Response(); + $mainResponse->setLastModified(new \DateTimeImmutable('-2 hour')); + $embeddedDate = new \DateTimeImmutable('-1 hour'); + $embeddedResponse = new Response(); + $embeddedResponse->setLastModified($embeddedDate); - // This master response uses the "validation" model - $masterResponse = new Response(); - $masterResponse->setLastModified(new \DateTimeImmutable('-2 hour')); - $masterResponse->setEtag('foo'); + $cacheStrategy->add($embeddedResponse); + $cacheStrategy->update($mainResponse); + + $this->assertTrue($mainResponse->headers->has('Last-Modified')); + $this->assertSame($embeddedDate->getTimestamp(), $mainResponse->getLastModified()->getTimestamp()); + } + + public function testLastModifiedIsRemovedWhenEmbeddedResponseHasNoLastModified() + { + $cacheStrategy = new ResponseCacheStrategy(); + + $mainResponse = new Response(); + $mainResponse->setLastModified(new \DateTimeImmutable('-2 hour')); - // Embedded response uses "expiry" model $embeddedResponse = new Response(); - $embeddedResponse->setLastModified($embeddedDate); + $cacheStrategy->add($embeddedResponse); + $cacheStrategy->update($mainResponse); + + $this->assertFalse($mainResponse->headers->has('Last-Modified')); + } - $cacheStrategy->update($masterResponse); + public function testLastModifiedIsNotAddedWhenMainResponseHasNoLastModified() + { + $cacheStrategy = new ResponseCacheStrategy(); - $this->assertTrue($masterResponse->isValidateable()); - $this->assertSame($embeddedDate->getTimestamp(), $masterResponse->getLastModified()->getTimestamp()); + $mainResponse = new Response(); + + $embeddedResponse = new Response(); + $embeddedResponse->setLastModified(new \DateTimeImmutable('-2 hour')); + + $cacheStrategy->add($embeddedResponse); + $cacheStrategy->update($mainResponse); + + $this->assertFalse($mainResponse->headers->has('Last-Modified')); } public function testMainResponseIsNotCacheableWhenEmbeddedResponseIsNotCacheable() diff --git a/src/Symfony/Component/Mailer/Bridge/Brevo/RemoteEvent/BrevoPayloadConverter.php b/src/Symfony/Component/Mailer/Bridge/Brevo/RemoteEvent/BrevoPayloadConverter.php index 5f5f0816c23c6..555cc046cd8c2 100644 --- a/src/Symfony/Component/Mailer/Bridge/Brevo/RemoteEvent/BrevoPayloadConverter.php +++ b/src/Symfony/Component/Mailer/Bridge/Brevo/RemoteEvent/BrevoPayloadConverter.php @@ -41,6 +41,7 @@ public function convert(array $payload): AbstractMailerEvent 'unique_opened' => MailerEngagementEvent::OPEN, 'opened' => MailerEngagementEvent::OPEN, 'proxy_open' => MailerEngagementEvent::OPEN, + 'unique_proxy_open' => MailerEngagementEvent::OPEN, 'complaint' => MailerEngagementEvent::SPAM, default => throw new ParseException(sprintf('Unsupported event "%s".', $payload['event'])), }; diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/PostgreSqlConnectionTest.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/PostgreSqlConnectionTest.php index 71dfcc3d1cb08..7e2e436403397 100644 --- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/PostgreSqlConnectionTest.php +++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/PostgreSqlConnectionTest.php @@ -11,8 +11,8 @@ namespace Symfony\Component\Messenger\Bridge\Doctrine\Tests\Transport; -use Doctrine\DBAL\Cache\ArrayResult; use Doctrine\DBAL\Cache\ArrayStatement; +use Doctrine\DBAL\Driver\Result as DriverResult; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\DBAL\Query\QueryBuilder; use Doctrine\DBAL\Result; @@ -98,10 +98,13 @@ public function countNotifyCalls() ->method('getNativeConnection') ->willReturn($wrappedConnection); + $driverResult = $this->createMock(DriverResult::class); + $driverResult->method('fetchAssociative') + ->willReturn(false); $driverConnection ->expects(self::any()) ->method('executeQuery') - ->willReturn(new Result(new ArrayResult([]), $driverConnection)); + ->willReturn(new Result($driverResult, $driverConnection)); } $connection = new PostgreSqlConnection(['table_name' => 'queue_table'], $driverConnection); diff --git a/src/Symfony/Component/Messenger/Bridge/Redis/Tests/Transport/RedisExtIntegrationTest.php b/src/Symfony/Component/Messenger/Bridge/Redis/Tests/Transport/RedisExtIntegrationTest.php index 998437cdda67d..87431f2abe61b 100644 --- a/src/Symfony/Component/Messenger/Bridge/Redis/Tests/Transport/RedisExtIntegrationTest.php +++ b/src/Symfony/Component/Messenger/Bridge/Redis/Tests/Transport/RedisExtIntegrationTest.php @@ -344,6 +344,9 @@ public function testJsonError() } } + /** + * @group transient-on-windows + */ public function testGetNonBlocking() { $redis = $this->createRedisClient(); @@ -360,6 +363,9 @@ public function testGetNonBlocking() } } + /** + * @group transient-on-windows + */ public function testGetAfterReject() { $redis = $this->createRedisClient(); @@ -379,6 +385,9 @@ public function testGetAfterReject() } } + /** + * @group transient-on-windows + */ public function testItProperlyHandlesEmptyMessages() { $redisReceiver = new RedisReceiver($this->connection, new Serializer()); diff --git a/src/Symfony/Component/Mime/RawMessage.php b/src/Symfony/Component/Mime/RawMessage.php index 484ffb0073f6a..2b1b52cde1226 100644 --- a/src/Symfony/Component/Mime/RawMessage.php +++ b/src/Symfony/Component/Mime/RawMessage.php @@ -18,11 +18,14 @@ */ class RawMessage { - /** @var iterable|string|resource */ + /** @var iterable|string|resource */ private $message; private bool $isGeneratorClosed; - public function __construct(iterable|string $message) + /** + * @param iterable|string|resource $message + */ + public function __construct(mixed $message) { $this->message = $message; } diff --git a/src/Symfony/Component/Mime/Tests/AddressTest.php b/src/Symfony/Component/Mime/Tests/AddressTest.php index dd3cf211c2671..baef170efc4f6 100644 --- a/src/Symfony/Component/Mime/Tests/AddressTest.php +++ b/src/Symfony/Component/Mime/Tests/AddressTest.php @@ -13,6 +13,8 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\Exception\InvalidArgumentException; +use Symfony\Component\Mime\Exception\RfcComplianceException; class AddressTest extends TestCase { @@ -32,7 +34,7 @@ public function testConstructor() public function testConstructorWithInvalidAddress() { - $this->expectException(\InvalidArgumentException::class); + $this->expectException(RfcComplianceException::class); new Address('fab pot@symfony.com'); } @@ -43,6 +45,14 @@ public function testCreate() $this->assertEquals($a, Address::create('fabien@symfony.com')); } + public function testCreateWithInvalidFormat() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Could not parse " + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Tests\Encoder; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Mime\Encoder\QpContentEncoder; + +class QpContentEncoderTest extends TestCase +{ + public function testReplaceLastChar() + { + $encoder = new QpContentEncoder(); + + $this->assertSame('message=09', $encoder->encodeString('message'.chr(0x09))); + $this->assertSame('message=20', $encoder->encodeString('message'.chr(0x20))); + } +} diff --git a/src/Symfony/Component/Mime/Tests/MessageTest.php b/src/Symfony/Component/Mime/Tests/MessageTest.php index 1d01fa4519e93..460da49b4471e 100644 --- a/src/Symfony/Component/Mime/Tests/MessageTest.php +++ b/src/Symfony/Component/Mime/Tests/MessageTest.php @@ -134,6 +134,16 @@ public function testGenerateMessageIdThrowsWhenHasFromButNoAddresses() $message->generateMessageId(); } + public function testGenerateMessageIdThrowsWhenNeitherFromNorSenderIsPresent() + { + $message = new Message(); + + $this->expectException(LogicException::class); + $this->expectExceptionMessage('An email must have a "From" or a "Sender" header.'); + + $message->generateMessageId(); + } + public function testToString() { $message = new Message(); diff --git a/src/Symfony/Component/Mime/Tests/Part/Multipart/FormDataPartTest.php b/src/Symfony/Component/Mime/Tests/Part/Multipart/FormDataPartTest.php index ddc2051bac91b..3c9aae6c0150e 100644 --- a/src/Symfony/Component/Mime/Tests/Part/Multipart/FormDataPartTest.php +++ b/src/Symfony/Component/Mime/Tests/Part/Multipart/FormDataPartTest.php @@ -243,4 +243,14 @@ public function testBoundaryContentTypeHeader() $headers[0] ); } + + public function testGetPartsThrowsOnUnexpectedFieldType() + { + $dataPart = new FormDataPart(['foo' => new \stdClass()]); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The value of the form field "foo" can only be a string, an array, or an instance of TextPart, "stdClass" given.'); + + $dataPart->getParts(); + } } diff --git a/src/Symfony/Component/Mime/Tests/RawMessageTest.php b/src/Symfony/Component/Mime/Tests/RawMessageTest.php index fa802f4710fc5..cfd97a5f4a2e8 100644 --- a/src/Symfony/Component/Mime/Tests/RawMessageTest.php +++ b/src/Symfony/Component/Mime/Tests/RawMessageTest.php @@ -80,6 +80,25 @@ public function testToIterableLegacy(mixed $messageParameter, bool $supportReuse } } + public function testToIterableOnResourceRewindsAndYieldsLines() + { + $handle = \fopen('php://memory', 'r+'); + \fwrite($handle, "line1\nline2\nline3\n"); + + $message = new RawMessage($handle); + $this->assertSame("line1\nline2\nline3\n", implode('', iterator_to_array($message->toIterable()))); + } + + public function testDestructClosesResource() + { + $handle = fopen('php://memory', 'r+'); + + $message = new RawMessage($handle); + unset($message); + + $this->assertIsClosedResource($handle); + } + public static function provideMessages(): array { return [ diff --git a/src/Symfony/Component/Mime/Tests/Test/Constraint/EmailAddressContainsTest.php b/src/Symfony/Component/Mime/Tests/Test/Constraint/EmailAddressContainsTest.php new file mode 100644 index 0000000000000..227a51f58a4b7 --- /dev/null +++ b/src/Symfony/Component/Mime/Tests/Test/Constraint/EmailAddressContainsTest.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Tests\Test\Constraint; + +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Mime\Email; +use Symfony\Component\Mime\Header\Headers; +use Symfony\Component\Mime\Test\Constraint\EmailAddressContains; + +class EmailAddressContainsTest extends TestCase +{ + public function testToString() + { + $constraint = new EmailAddressContains('headerName', 'expectedValue'); + + $this->assertSame('contains address "headerName" with value "expectedValue"', $constraint->toString()); + } + + public function testFailureDescription() + { + $mailboxHeader = 'text@example.com'; + $headers = new Headers(); + $headers->addMailboxHeader($mailboxHeader, 'actualValue@example.com'); + + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage('Failed asserting that the Email contains address "text@example.com" with value "expectedValue@example.com" (value is actualValue@example.com).'); + + (new EmailAddressContains($mailboxHeader, 'expectedValue@example.com'))->evaluate(new Email($headers)); + } +} diff --git a/src/Symfony/Component/Mime/Tests/Test/Constraint/EmailAttachmentCountTest.php b/src/Symfony/Component/Mime/Tests/Test/Constraint/EmailAttachmentCountTest.php new file mode 100644 index 0000000000000..60976675ab3d5 --- /dev/null +++ b/src/Symfony/Component/Mime/Tests/Test/Constraint/EmailAttachmentCountTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Tests\Test\Constraint; + +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Mime\Email; +use Symfony\Component\Mime\Test\Constraint\EmailAttachmentCount; + +class EmailAttachmentCountTest extends TestCase +{ + public function testToString() + { + $constraint = new EmailAttachmentCount(1); + + $this->assertSame('has sent "1" attachment(s)', $constraint->toString()); + } + + public function testFailureDescription() + { + $email = new Email(); + $email->attach('attachment content', 'attachment.txt'); + + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage('Failed asserting that the Email has sent "2" attachment(s).'); + + (new EmailAttachmentCount(2))->evaluate($email); + } +} diff --git a/src/Symfony/Component/Mime/Tests/Test/Constraint/EmailHasHeaderTest.php b/src/Symfony/Component/Mime/Tests/Test/Constraint/EmailHasHeaderTest.php new file mode 100644 index 0000000000000..ae5f75fddfb55 --- /dev/null +++ b/src/Symfony/Component/Mime/Tests/Test/Constraint/EmailHasHeaderTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Tests\Test\Constraint; + +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Mime\Email; +use Symfony\Component\Mime\Header\Headers; +use Symfony\Component\Mime\Test\Constraint\EmailHasHeader; + +class EmailHasHeaderTest extends TestCase +{ + public function testToString() + { + $constraint = new EmailHasHeader('headerName'); + + $this->assertSame('has header "headerName"', $constraint->toString()); + } + + public function testFailureDescription() + { + $headers = new Headers(); + $headers->addMailboxHeader('headerName', 'test@example.com'); + + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage('Failed asserting that the Email has header "not existing header".'); + + (new EmailHasHeader('not existing header'))->evaluate(new Email($headers)); + } +} diff --git a/src/Symfony/Component/Mime/Tests/Test/Constraint/EmailHtmlBodyContainsTest.php b/src/Symfony/Component/Mime/Tests/Test/Constraint/EmailHtmlBodyContainsTest.php new file mode 100644 index 0000000000000..ae994b0959796 --- /dev/null +++ b/src/Symfony/Component/Mime/Tests/Test/Constraint/EmailHtmlBodyContainsTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Tests\Test\Constraint; + +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Mime\Email; +use Symfony\Component\Mime\Test\Constraint\EmailHtmlBodyContains; + +class EmailHtmlBodyContainsTest extends TestCase +{ + public function testToString() + { + $constraint = new EmailHtmlBodyContains('expectedValue'); + + $this->assertSame('contains "expectedValue"', $constraint->toString()); + } + + public function testFailureDescription() + { + $expectedValue = 'expectedValue'; + $email = new Email(); + $email->html('actualValue')->text($expectedValue); + + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage('Failed asserting that the Email HTML body contains "expectedValue".'); + + (new EmailHtmlBodyContains($expectedValue))->evaluate($email); + } +} diff --git a/src/Symfony/Component/Mime/Tests/Test/Constraint/EmailTextBodyContainsTest.php b/src/Symfony/Component/Mime/Tests/Test/Constraint/EmailTextBodyContainsTest.php new file mode 100644 index 0000000000000..43ba0170ef52c --- /dev/null +++ b/src/Symfony/Component/Mime/Tests/Test/Constraint/EmailTextBodyContainsTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Tests\Test\Constraint; + +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Mime\Email; +use Symfony\Component\Mime\Test\Constraint\EmailTextBodyContains; + +class EmailTextBodyContainsTest extends TestCase +{ + public function testToString() + { + $constraint = new EmailTextBodyContains('expectedValue'); + + $this->assertSame('contains "expectedValue"', $constraint->toString()); + } + + public function testFailureDescription() + { + $expectedValue = 'expectedValue'; + $email = new Email(); + $email->html($expectedValue)->text('actualValue'); + + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage('Failed asserting that the Email text body contains "expectedValue".'); + + (new EmailTextBodyContains($expectedValue))->evaluate($email); + } +} diff --git a/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php b/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php index a136a3bd5abc5..c9502b7d2a342 100644 --- a/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php +++ b/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php @@ -114,7 +114,7 @@ class UnsupportedSchemeException extends LogicException ], 'kaz-info-teh' => [ 'class' => Bridge\KazInfoTeh\KazInfoTehTransportFactory::class, - 'package' => 'symfony/symfony/kaz-info-teh-notifier', + 'package' => 'symfony/kaz-info-teh-notifier', ], 'lightsms' => [ 'class' => Bridge\LightSms\LightSmsTransportFactory::class, diff --git a/src/Symfony/Component/Notifier/Tests/Exception/UnsupportedSchemeExceptionTest.php b/src/Symfony/Component/Notifier/Tests/Exception/UnsupportedSchemeExceptionTest.php index f108d758bb0d2..52f9a9b085087 100644 --- a/src/Symfony/Component/Notifier/Tests/Exception/UnsupportedSchemeExceptionTest.php +++ b/src/Symfony/Component/Notifier/Tests/Exception/UnsupportedSchemeExceptionTest.php @@ -118,14 +118,18 @@ public static function messageWhereSchemeIsPartOfSchemeToPackageMapProvider(): \ yield ['sns', 'symfony/amazon-sns-notifier']; yield ['bandwidth', 'symfony/bandwidth-notifier']; yield ['brevo', 'symfony/brevo-notifier']; + yield ['chatwork', 'symfony/chatwork-notifier']; yield ['clickatell', 'symfony/clickatell-notifier']; yield ['clicksend', 'symfony/click-send-notifier']; yield ['contact-everyone', 'symfony/contact-everyone-notifier']; yield ['discord', 'symfony/discord-notifier']; + yield ['engagespot', 'symfony/engagespot-notifier']; yield ['esendex', 'symfony/esendex-notifier']; + yield ['expo', 'symfony/expo-notifier']; yield ['fakechat', 'symfony/fake-chat-notifier']; yield ['fakesms', 'symfony/fake-sms-notifier']; yield ['firebase', 'symfony/firebase-notifier']; + yield ['forty-six-elks', 'symfony/forty-six-elks-notifier']; yield ['freemobile', 'symfony/free-mobile-notifier']; yield ['gatewayapi', 'symfony/gateway-api-notifier']; yield ['gitter', 'symfony/gitter-notifier']; @@ -133,6 +137,7 @@ public static function messageWhereSchemeIsPartOfSchemeToPackageMapProvider(): \ yield ['infobip', 'symfony/infobip-notifier']; yield ['iqsms', 'symfony/iqsms-notifier']; yield ['isendpro', 'symfony/isendpro-notifier']; + yield ['kaz-info-teh', 'symfony/kaz-info-teh-notifier']; yield ['lightsms', 'symfony/light-sms-notifier']; yield ['linenotify', 'symfony/line-notify-notifier']; yield ['linkedin', 'symfony/linked-in-notifier']; @@ -148,8 +153,11 @@ public static function messageWhereSchemeIsPartOfSchemeToPackageMapProvider(): \ yield ['ntfy', 'symfony/ntfy-notifier']; yield ['octopush', 'symfony/octopush-notifier']; yield ['onesignal', 'symfony/one-signal-notifier']; + yield ['orange-sms', 'symfony/orange-sms-notifier']; yield ['ovhcloud', 'symfony/ovh-cloud-notifier']; + yield ['pagerduty', 'symfony/pager-duty-notifier']; yield ['plivo', 'symfony/plivo-notifier']; + yield ['pushover', 'symfony/pushover-notifier']; yield ['redlink', 'symfony/redlink-notifier']; yield ['ringcentral', 'symfony/ring-central-notifier']; yield ['rocketchat', 'symfony/rocket-chat-notifier']; @@ -171,6 +179,8 @@ public static function messageWhereSchemeIsPartOfSchemeToPackageMapProvider(): \ yield ['turbosms', 'symfony/turbo-sms-notifier']; yield ['twilio', 'symfony/twilio-notifier']; yield ['twitter', 'symfony/twitter-notifier']; + yield ['vonage', 'symfony/vonage-notifier']; + yield ['yunpian', 'symfony/yunpian-notifier']; yield ['zendesk', 'symfony/zendesk-notifier']; yield ['zulip', 'symfony/zulip-notifier']; yield ['goip', 'symfony/go-ip-notifier']; diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 4343c9fc313fe..6d1ebaf1a96d2 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -95,7 +95,7 @@ public function getValue(object|array $objectOrArray, string|PropertyPathInterfa self::VALUE => $objectOrArray, ]; - if (\is_object($objectOrArray) && false === strpbrk((string) $propertyPath, '.[?')) { + if (\is_object($objectOrArray) && (false === strpbrk((string) $propertyPath, '.[?') || $objectOrArray instanceof \stdClass && property_exists($objectOrArray, $propertyPath))) { return $this->readProperty($zval, $propertyPath, $this->ignoreInvalidProperty)[self::VALUE]; } @@ -111,7 +111,7 @@ public function getValue(object|array $objectOrArray, string|PropertyPathInterfa */ public function setValue(object|array &$objectOrArray, string|PropertyPathInterface $propertyPath, mixed $value) { - if (\is_object($objectOrArray) && false === strpbrk((string) $propertyPath, '.[')) { + if (\is_object($objectOrArray) && (false === strpbrk((string) $propertyPath, '.[') || $objectOrArray instanceof \stdClass && property_exists($objectOrArray, $propertyPath))) { $zval = [ self::VALUE => $objectOrArray, ]; @@ -214,7 +214,13 @@ public function isReadable(object|array $objectOrArray, string|PropertyPathInter $zval = [ self::VALUE => $objectOrArray, ]; - $this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength(), $this->ignoreInvalidIndices); + + // handle stdClass with properties with a dot in the name + if ($objectOrArray instanceof \stdClass && str_contains($propertyPath, '.') && property_exists($objectOrArray, $propertyPath)) { + $this->readProperty($zval, $propertyPath, $this->ignoreInvalidProperty); + } else { + $this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength(), $this->ignoreInvalidIndices); + } return true; } catch (AccessException) { @@ -232,6 +238,14 @@ public function isWritable(object|array $objectOrArray, string|PropertyPathInter $zval = [ self::VALUE => $objectOrArray, ]; + + // handle stdClass with properties with a dot in the name + if ($objectOrArray instanceof \stdClass && str_contains($propertyPath, '.') && property_exists($objectOrArray, $propertyPath)) { + $this->readProperty($zval, $propertyPath, $this->ignoreInvalidProperty); + + return true; + } + $propertyValues = $this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength() - 1); for ($i = \count($propertyValues) - 1; 0 <= $i; --$i) { diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php index dc5c5500b18ea..9fca6f8275102 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -526,6 +526,7 @@ public static function getValidWritePropertyPaths() [['firstName' => 'Bernhard'], '[firstName]', 'Bernhard'], [['index' => ['firstName' => 'Bernhard']], '[index][firstName]', 'Bernhard'], [(object) ['firstName' => 'Bernhard'], 'firstName', 'Bernhard'], + [(object) ['first.Name' => 'Bernhard'], 'first.Name', 'Bernhard'], [(object) ['property' => ['firstName' => 'Bernhard']], 'property[firstName]', 'Bernhard'], [['index' => (object) ['firstName' => 'Bernhard']], '[index].firstName', 'Bernhard'], [(object) ['property' => (object) ['firstName' => 'Bernhard']], 'property.firstName', 'Bernhard'], diff --git a/src/Symfony/Component/Routing/Loader/AttributeFileLoader.php b/src/Symfony/Component/Routing/Loader/AttributeFileLoader.php index 34d8b52deac1d..e9a13e597256f 100644 --- a/src/Symfony/Component/Routing/Loader/AttributeFileLoader.php +++ b/src/Symfony/Component/Routing/Loader/AttributeFileLoader.php @@ -82,7 +82,7 @@ protected function findClass(string $file): string|false $tokens = token_get_all(file_get_contents($file)); if (1 === \count($tokens) && \T_INLINE_HTML === $tokens[0][0]) { - throw new \InvalidArgumentException(sprintf('The file "%s" does not contain PHP code. Did you forgot to add the " true, \T_STRING => true]; diff --git a/src/Symfony/Component/Routing/Tests/Loader/AttributeFileLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/AttributeFileLoaderTest.php index 64dfaccc6763b..33626b120902a 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/AttributeFileLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/AttributeFileLoaderTest.php @@ -58,7 +58,7 @@ public function testLoadTraitWithClassConstant() public function testLoadFileWithoutStartTag() { $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('Did you forgot to add the "expectExceptionMessage('Did you forget to add the "loader->load(__DIR__.'/../Fixtures/OtherAnnotatedClasses/NoStartTagClass.php'); } diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.it.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.it.xlf index ef250923e411c..72eace25e814a 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.it.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.it.xlf @@ -76,7 +76,7 @@ Too many failed login attempts, please try again in %minutes% minutes. - Troppi tentativi di accesso falliti, riprova tra %minutes% minuti. + Troppi tentativi di login falliti, riprova tra %minutes% minuti. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.nl.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.nl.xlf index 4549d9f1c34f3..49b7aa78dbf0b 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.nl.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.nl.xlf @@ -76,7 +76,7 @@ Too many failed login attempts, please try again in %minutes% minutes. - Te veel mislukte inlogpogingen, probeer het over %minutes% minuten opnieuw. + Te veel onjuiste inlogpogingen, probeer het opnieuw over %minutes% minuten. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.sr_Cyrl.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.sr_Cyrl.xlf index c4e58def93226..2192fe6e00b0c 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.sr_Cyrl.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.sr_Cyrl.xlf @@ -76,7 +76,7 @@ Too many failed login attempts, please try again in %minutes% minutes. - Превише неуспешних покушаја пријављивања, покушајте поново за %minutes% минут.|Превише неуспешних покушаја пријављивања, покушајте поново за %minutes% минута.|Превише неуспешних покушаја пријављивања, покушајте поново за %minutes% минута. + Превише неуспешних покушаја пријављивања, покушајте поново за %minutes% минут.|Превише неуспешних покушаја пријављивања, покушајте поново за %minutes% минута. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.sr_Latn.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.sr_Latn.xlf index ad0774f9a0bb2..6a925c5b0fbaf 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.sr_Latn.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.sr_Latn.xlf @@ -76,7 +76,7 @@ Too many failed login attempts, please try again in %minutes% minutes. - Previše neuspešnih pokušaja prijavljivanja, pokušajte ponovo za %minutes% minut.|Previše neuspešnih pokušaja prijavljivanja, pokušajte ponovo za %minutes% minuta.|Previše neuspešnih pokušaja prijavljivanja, pokušajte ponovo za %minutes% minuta. + Previše neuspešnih pokušaja prijavljivanja, pokušajte ponovo za %minutes% minut.|Previše neuspešnih pokušaja prijavljivanja, pokušajte ponovo za %minutes% minuta. diff --git a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php index 068beac1118c1..7aeec196c672b 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php @@ -87,7 +87,7 @@ public function authenticate(RequestEvent $event): void } $request = $event->getRequest(); - $session = !$request->attributes->getBoolean('_stateless') && $request->hasPreviousSession() ? $request->getSession() : null; + $session = $request->hasPreviousSession() ? $request->getSession() : null; $request->attributes->set('_security_firewall_run', $this->sessionKey); diff --git a/src/Symfony/Component/Serializer/DataCollector/SerializerDataCollector.php b/src/Symfony/Component/Serializer/DataCollector/SerializerDataCollector.php index 671239d28c7fb..2880dea37d8f2 100644 --- a/src/Symfony/Component/Serializer/DataCollector/SerializerDataCollector.php +++ b/src/Symfony/Component/Serializer/DataCollector/SerializerDataCollector.php @@ -21,7 +21,7 @@ /** * @author Mathias Arlaud * - * @internal + * @final */ class SerializerDataCollector extends DataCollector implements LateDataCollectorInterface { diff --git a/src/Symfony/Component/Serializer/Debug/TraceableEncoder.php b/src/Symfony/Component/Serializer/Debug/TraceableEncoder.php index 0795d14ca0cfa..afefee0ee6fb6 100644 --- a/src/Symfony/Component/Serializer/Debug/TraceableEncoder.php +++ b/src/Symfony/Component/Serializer/Debug/TraceableEncoder.php @@ -23,7 +23,7 @@ * * @author Mathias Arlaud * - * @internal + * @final */ class TraceableEncoder implements EncoderInterface, DecoderInterface, SerializerAwareInterface { diff --git a/src/Symfony/Component/Serializer/Debug/TraceableNormalizer.php b/src/Symfony/Component/Serializer/Debug/TraceableNormalizer.php index fc4db40ad1988..88ab4863d2eac 100644 --- a/src/Symfony/Component/Serializer/Debug/TraceableNormalizer.php +++ b/src/Symfony/Component/Serializer/Debug/TraceableNormalizer.php @@ -25,7 +25,7 @@ * * @author Mathias Arlaud * - * @internal + * @final */ class TraceableNormalizer implements NormalizerInterface, DenormalizerInterface, SerializerAwareInterface, NormalizerAwareInterface, DenormalizerAwareInterface, CacheableSupportsMethodInterface { diff --git a/src/Symfony/Component/Serializer/Debug/TraceableSerializer.php b/src/Symfony/Component/Serializer/Debug/TraceableSerializer.php index 789ae65ca0aa8..dd22e8678e782 100644 --- a/src/Symfony/Component/Serializer/Debug/TraceableSerializer.php +++ b/src/Symfony/Component/Serializer/Debug/TraceableSerializer.php @@ -24,7 +24,7 @@ * * @author Mathias Arlaud * - * @internal + * @final */ class TraceableSerializer implements SerializerInterface, NormalizerInterface, DenormalizerInterface, EncoderInterface, DecoderInterface { diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php index 13c0dcc08d0ed..c0be73a8bd685 100644 --- a/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php @@ -149,7 +149,7 @@ public function testEncodeCustomSettings() $this->encoder = new CsvEncoder([ CsvEncoder::DELIMITER_KEY => ';', CsvEncoder::ENCLOSURE_KEY => "'", - CsvEncoder::ESCAPE_CHAR_KEY => '|', + CsvEncoder::ESCAPE_CHAR_KEY => '', CsvEncoder::KEY_SEPARATOR_KEY => '-', ]); @@ -175,7 +175,7 @@ public function testEncodeCustomSettingsPassedInContext() , $this->encoder->encode($value, 'csv', [ CsvEncoder::DELIMITER_KEY => ';', CsvEncoder::ENCLOSURE_KEY => "'", - CsvEncoder::ESCAPE_CHAR_KEY => '|', + CsvEncoder::ESCAPE_CHAR_KEY => '', CsvEncoder::KEY_SEPARATOR_KEY => '-', ])); } @@ -185,7 +185,7 @@ public function testEncodeCustomSettingsPassedInConstructor() $encoder = new CsvEncoder([ CsvEncoder::DELIMITER_KEY => ';', CsvEncoder::ENCLOSURE_KEY => "'", - CsvEncoder::ESCAPE_CHAR_KEY => '|', + CsvEncoder::ESCAPE_CHAR_KEY => '', CsvEncoder::KEY_SEPARATOR_KEY => '-', ]); $value = ['a' => 'he\'llo', 'c' => ['d' => 'foo']]; @@ -574,7 +574,7 @@ public function testDecodeCustomSettings() $this->encoder = new CsvEncoder([ CsvEncoder::DELIMITER_KEY => ';', CsvEncoder::ENCLOSURE_KEY => "'", - CsvEncoder::ESCAPE_CHAR_KEY => '|', + CsvEncoder::ESCAPE_CHAR_KEY => '', CsvEncoder::KEY_SEPARATOR_KEY => '-', ]); @@ -596,7 +596,7 @@ public function testDecodeCustomSettingsPassedInContext() , 'csv', [ CsvEncoder::DELIMITER_KEY => ';', CsvEncoder::ENCLOSURE_KEY => "'", - CsvEncoder::ESCAPE_CHAR_KEY => '|', + CsvEncoder::ESCAPE_CHAR_KEY => '', CsvEncoder::KEY_SEPARATOR_KEY => '-', ])); } @@ -606,7 +606,7 @@ public function testDecodeCustomSettingsPassedInConstructor() $encoder = new CsvEncoder([ CsvEncoder::DELIMITER_KEY => ';', CsvEncoder::ENCLOSURE_KEY => "'", - CsvEncoder::ESCAPE_CHAR_KEY => '|', + CsvEncoder::ESCAPE_CHAR_KEY => '', CsvEncoder::KEY_SEPARATOR_KEY => '-', CsvEncoder::AS_COLLECTION_KEY => true, // Can be removed in 5.0 ]); diff --git a/src/Symfony/Component/String/Inflector/EnglishInflector.php b/src/Symfony/Component/String/Inflector/EnglishInflector.php index 77ebc134a436f..c41badeadead6 100644 --- a/src/Symfony/Component/String/Inflector/EnglishInflector.php +++ b/src/Symfony/Component/String/Inflector/EnglishInflector.php @@ -37,6 +37,9 @@ final class EnglishInflector implements InflectorInterface // curricula (curriculum) ['alucirruc', 9, true, true, 'curriculum'], + // quora (quorum) + ['arouq', 5, true, true, 'quorum'], + // genera (genus) ['areneg', 6, true, true, 'genus'], @@ -121,6 +124,9 @@ final class EnglishInflector implements InflectorInterface // statuses (status) ['sesutats', 8, true, true, 'status'], + // article (articles), ancle (ancles) + ['sel', 3, true, true, 'le'], + // analyses (analysis), ellipses (ellipsis), fungi (fungus), // neuroses (neurosis), theses (thesis), emphases (emphasis), // oases (oasis), crises (crisis), houses (house), bases (base), @@ -265,6 +271,9 @@ final class EnglishInflector implements InflectorInterface // albums (album) ['mubla', 5, true, true, 'albums'], + // quorums (quorum) + ['murouq', 6, true, true, ['quora', 'quorums']], + // bacteria (bacterium), curricula (curriculum), media (medium), memoranda (memorandum), phenomena (phenomenon), strata (stratum) ['mu', 2, true, true, 'a'], diff --git a/src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php b/src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php index fb5d04300305a..4a6895f32f20e 100644 --- a/src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php +++ b/src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php @@ -28,10 +28,12 @@ public static function singularizeProvider() ['alumnae', 'alumna'], ['alumni', 'alumnus'], ['analyses', ['analys', 'analyse', 'analysis']], + ['ankles', 'ankle'], ['antennae', 'antenna'], ['antennas', 'antenna'], ['appendices', ['appendex', 'appendix', 'appendice']], ['arches', ['arch', 'arche']], + ['articles', 'article'], ['atlases', ['atlas', 'atlase', 'atlasis']], ['axes', ['ax', 'axe', 'axis']], ['babies', 'baby'], @@ -134,6 +136,8 @@ public static function singularizeProvider() ['poppies', 'poppy'], ['prices', ['prex', 'prix', 'price']], ['quizzes', 'quiz'], + ['quora', 'quorum'], + ['quorums', 'quorum'], ['radii', 'radius'], ['roofs', 'roof'], ['roses', ['ros', 'rose', 'rosis']], @@ -189,9 +193,11 @@ public static function pluralizeProvider() ['album', 'albums'], ['alumnus', 'alumni'], ['analysis', 'analyses'], + ['ankle', 'ankles'], ['antenna', 'antennas'], // antennae ['appendix', ['appendicies', 'appendixes']], ['arch', 'arches'], + ['article', 'articles'], ['atlas', 'atlases'], ['axe', 'axes'], ['axis', 'axes'], @@ -285,6 +291,7 @@ public static function pluralizeProvider() ['poppy', 'poppies'], ['price', 'prices'], ['quiz', 'quizzes'], + ['quorum', ['quora', 'quorums']], ['radius', 'radii'], ['roof', ['roofs', 'rooves']], ['rose', 'roses'], diff --git a/src/Symfony/Component/Uid/Tests/UuidTest.php b/src/Symfony/Component/Uid/Tests/UuidTest.php index 5e05b89f6e395..cb7ac1088541d 100644 --- a/src/Symfony/Component/Uid/Tests/UuidTest.php +++ b/src/Symfony/Component/Uid/Tests/UuidTest.php @@ -95,6 +95,15 @@ public function testV1() $this->assertSame('3499710062d0', $uuid->getNode()); } + public function testV1IsLowerCase() + { + $uuid = new UuidV1(); + $this->assertSame(strtolower((string) $uuid), (string) $uuid); + + $uuid = new UuidV1('D9E7A184-5D5B-11EA-A62A-3499710062D0'); + $this->assertSame(strtolower((string) $uuid), (string) $uuid); + } + public function testV3() { $uuid = Uuid::v3(new UuidV4(self::A_UUID_V4), 'the name'); diff --git a/src/Symfony/Component/Uid/UuidV1.php b/src/Symfony/Component/Uid/UuidV1.php index 1ec004168240d..1e68737085b4b 100644 --- a/src/Symfony/Component/Uid/UuidV1.php +++ b/src/Symfony/Component/Uid/UuidV1.php @@ -25,7 +25,7 @@ class UuidV1 extends Uuid implements TimeBasedUidInterface public function __construct(?string $uuid = null) { if (null === $uuid) { - $this->uid = uuid_create(static::TYPE); + $this->uid = strtolower(uuid_create(static::TYPE)); } else { parent::__construct($uuid, true); } diff --git a/src/Symfony/Component/Validator/Constraints/AtLeastOneOfValidator.php b/src/Symfony/Component/Validator/Constraints/AtLeastOneOfValidator.php index 94ad5eacab9e7..5348527c70685 100644 --- a/src/Symfony/Component/Validator/Constraints/AtLeastOneOfValidator.php +++ b/src/Symfony/Component/Validator/Constraints/AtLeastOneOfValidator.php @@ -42,9 +42,11 @@ public function validate(mixed $value, Constraint $constraint) continue; } + $context = $this->context; $executionContext = clone $this->context; $executionContext->setNode($value, $this->context->getObject(), $this->context->getMetadata(), $this->context->getPropertyPath()); $violations = $validator->inContext($executionContext)->validate($value, $item, $this->context->getGroup())->getViolations(); + $this->context = $context; if (\count($this->context->getViolations()) === \count($violations)) { return; diff --git a/src/Symfony/Component/Validator/Constraints/CardSchemeValidator.php b/src/Symfony/Component/Validator/Constraints/CardSchemeValidator.php index 3e51050158736..22de0d8db7799 100644 --- a/src/Symfony/Component/Validator/Constraints/CardSchemeValidator.php +++ b/src/Symfony/Component/Validator/Constraints/CardSchemeValidator.php @@ -29,65 +29,65 @@ class CardSchemeValidator extends ConstraintValidator protected $schemes = [ // American Express card numbers start with 34 or 37 and have 15 digits. CardScheme::AMEX => [ - '/^3[47][0-9]{13}$/', + '/^3[47][0-9]{13}$/D', ], // China UnionPay cards start with 62 and have between 16 and 19 digits. // Please note that these cards do not follow Luhn Algorithm as a checksum. CardScheme::CHINA_UNIONPAY => [ - '/^62[0-9]{14,17}$/', + '/^62[0-9]{14,17}$/D', ], // Diners Club card numbers begin with 300 through 305, 36 or 38. All have 14 digits. // There are Diners Club cards that begin with 5 and have 16 digits. // These are a joint venture between Diners Club and MasterCard, and should be processed like a MasterCard. CardScheme::DINERS => [ - '/^3(?:0[0-5]|[68][0-9])[0-9]{11}$/', + '/^3(?:0[0-5]|[68][0-9])[0-9]{11}$/D', ], // Discover card numbers begin with 6011, 622126 through 622925, 644 through 649 or 65. // All have 16 digits. CardScheme::DISCOVER => [ - '/^6011[0-9]{12}$/', - '/^64[4-9][0-9]{13}$/', - '/^65[0-9]{14}$/', - '/^622(12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|91[0-9]|92[0-5])[0-9]{10}$/', + '/^6011[0-9]{12}$/D', + '/^64[4-9][0-9]{13}$/D', + '/^65[0-9]{14}$/D', + '/^622(12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|91[0-9]|92[0-5])[0-9]{10}$/D', ], // InstaPayment cards begin with 637 through 639 and have 16 digits. CardScheme::INSTAPAYMENT => [ - '/^63[7-9][0-9]{13}$/', + '/^63[7-9][0-9]{13}$/D', ], // JCB cards beginning with 2131 or 1800 have 15 digits. // JCB cards beginning with 35 have 16 digits. CardScheme::JCB => [ - '/^(?:2131|1800|35[0-9]{3})[0-9]{11}$/', + '/^(?:2131|1800|35[0-9]{3})[0-9]{11}$/D', ], // Laser cards begin with either 6304, 6706, 6709 or 6771 and have between 16 and 19 digits. CardScheme::LASER => [ - '/^(6304|670[69]|6771)[0-9]{12,15}$/', + '/^(6304|670[69]|6771)[0-9]{12,15}$/D', ], // Maestro international cards begin with 675900..675999 and have between 12 and 19 digits. // Maestro UK cards begin with either 500000..509999 or 560000..699999 and have between 12 and 19 digits. CardScheme::MAESTRO => [ - '/^(6759[0-9]{2})[0-9]{6,13}$/', - '/^(50[0-9]{4})[0-9]{6,13}$/', - '/^5[6-9][0-9]{10,17}$/', - '/^6[0-9]{11,18}$/', + '/^(6759[0-9]{2})[0-9]{6,13}$/D', + '/^(50[0-9]{4})[0-9]{6,13}$/D', + '/^5[6-9][0-9]{10,17}$/D', + '/^6[0-9]{11,18}$/D', ], // All MasterCard numbers start with the numbers 51 through 55. All have 16 digits. // October 2016 MasterCard numbers can also start with 222100 through 272099. CardScheme::MASTERCARD => [ - '/^5[1-5][0-9]{14}$/', - '/^2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12})$/', + '/^5[1-5][0-9]{14}$/D', + '/^2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12})$/D', ], // Payment system MIR numbers start with 220, then 1 digit from 0 to 4, then between 12 and 15 digits CardScheme::MIR => [ - '/^220[0-4][0-9]{12,15}$/', + '/^220[0-4][0-9]{12,15}$/D', ], // All UATP card numbers start with a 1 and have a length of 15 digits. CardScheme::UATP => [ - '/^1[0-9]{14}$/', + '/^1[0-9]{14}$/D', ], // All Visa card numbers start with a 4 and have a length of 13, 16, or 19 digits. CardScheme::VISA => [ - '/^4([0-9]{12}|[0-9]{15}|[0-9]{18})$/', + '/^4([0-9]{12}|[0-9]{15}|[0-9]{18})$/D', ], ]; diff --git a/src/Symfony/Component/Validator/Constraints/CssColorValidator.php b/src/Symfony/Component/Validator/Constraints/CssColorValidator.php index 9e8b1b55c194c..78563a92c31bb 100644 --- a/src/Symfony/Component/Validator/Constraints/CssColorValidator.php +++ b/src/Symfony/Component/Validator/Constraints/CssColorValidator.php @@ -21,21 +21,21 @@ */ class CssColorValidator extends ConstraintValidator { - private const PATTERN_HEX_LONG = '/^#[0-9a-f]{6}$/i'; - private const PATTERN_HEX_LONG_WITH_ALPHA = '/^#[0-9a-f]{8}$/i'; - private const PATTERN_HEX_SHORT = '/^#[0-9a-f]{3}$/i'; - private const PATTERN_HEX_SHORT_WITH_ALPHA = '/^#[0-9a-f]{4}$/i'; + private const PATTERN_HEX_LONG = '/^#[0-9a-f]{6}$/iD'; + private const PATTERN_HEX_LONG_WITH_ALPHA = '/^#[0-9a-f]{8}$/iD'; + private const PATTERN_HEX_SHORT = '/^#[0-9a-f]{3}$/iD'; + private const PATTERN_HEX_SHORT_WITH_ALPHA = '/^#[0-9a-f]{4}$/iD'; // List comes from https://www.w3.org/wiki/CSS/Properties/color/keywords#Basic_Colors - private const PATTERN_BASIC_NAMED_COLORS = '/^(black|silver|gray|white|maroon|red|purple|fuchsia|green|lime|olive|yellow|navy|blue|teal|aqua)$/i'; + private const PATTERN_BASIC_NAMED_COLORS = '/^(black|silver|gray|white|maroon|red|purple|fuchsia|green|lime|olive|yellow|navy|blue|teal|aqua)$/iD'; // List comes from https://www.w3.org/wiki/CSS/Properties/color/keywords#Extended_colors - private const PATTERN_EXTENDED_NAMED_COLORS = '/^(aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgreen|darkgrey|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|gray|green|greenyellow|grey|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgray|lightgreen|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightslategrey|lightsteelblue|lightyellow|lime|limegreen|linen|magenta|maroon|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|navy|oldlace|olive|olivedrab|orange|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|purple|red|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|slategrey|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|wheat|white|whitesmoke|yellow|yellowgreen)$/i'; + private const PATTERN_EXTENDED_NAMED_COLORS = '/^(aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgreen|darkgrey|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|gray|green|greenyellow|grey|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgray|lightgreen|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightslategrey|lightsteelblue|lightyellow|lime|limegreen|linen|magenta|maroon|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|navy|oldlace|olive|olivedrab|orange|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|purple|red|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|slategrey|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|wheat|white|whitesmoke|yellow|yellowgreen)$/iD'; // List comes from https://drafts.csswg.org/css-color/#css-system-colors - private const PATTERN_SYSTEM_COLORS = '/^(Canvas|CanvasText|LinkText|VisitedText|ActiveText|ButtonFace|ButtonText|ButtonBorder|Field|FieldText|Highlight|HighlightText|SelectedItem|SelectedItemText|Mark|MarkText|GrayText)$/i'; - private const PATTERN_KEYWORDS = '/^(transparent|currentColor)$/i'; - private const PATTERN_RGB = '/^rgb\(\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d)\s*\)$/i'; - private const PATTERN_RGBA = '/^rgba\(\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|0?\.\d+|1(\.0)?)\s*\)$/i'; - private const PATTERN_HSL = '/^hsl\(\s*(0|360|35\d|3[0-4]\d|[12]\d\d|0?\d?\d),\s*(0|100|\d{1,2})%,\s*(0|100|\d{1,2})%\s*\)$/i'; - private const PATTERN_HSLA = '/^hsla\(\s*(0|360|35\d|3[0-4]\d|[12]\d\d|0?\d?\d),\s*(0|100|\d{1,2})%,\s*(0|100|\d{1,2})%,\s*(0|0?\.\d+|1(\.0)?)\s*\)$/i'; + private const PATTERN_SYSTEM_COLORS = '/^(Canvas|CanvasText|LinkText|VisitedText|ActiveText|ButtonFace|ButtonText|ButtonBorder|Field|FieldText|Highlight|HighlightText|SelectedItem|SelectedItemText|Mark|MarkText|GrayText)$/iD'; + private const PATTERN_KEYWORDS = '/^(transparent|currentColor)$/iD'; + private const PATTERN_RGB = '/^rgb\(\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d)\s*\)$/iD'; + private const PATTERN_RGBA = '/^rgba\(\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|0?\.\d+|1(\.0)?)\s*\)$/iD'; + private const PATTERN_HSL = '/^hsl\(\s*(0|360|35\d|3[0-4]\d|[12]\d\d|0?\d?\d),\s*(0|100|\d{1,2})%,\s*(0|100|\d{1,2})%\s*\)$/iD'; + private const PATTERN_HSLA = '/^hsla\(\s*(0|360|35\d|3[0-4]\d|[12]\d\d|0?\d?\d),\s*(0|100|\d{1,2})%,\s*(0|100|\d{1,2})%,\s*(0|0?\.\d+|1(\.0)?)\s*\)$/iD'; private const COLOR_PATTERNS = [ CssColor::HEX_LONG => self::PATTERN_HEX_LONG, diff --git a/src/Symfony/Component/Validator/Constraints/DateValidator.php b/src/Symfony/Component/Validator/Constraints/DateValidator.php index 0e3d8484303d0..65dc9648ac40f 100644 --- a/src/Symfony/Component/Validator/Constraints/DateValidator.php +++ b/src/Symfony/Component/Validator/Constraints/DateValidator.php @@ -21,7 +21,7 @@ */ class DateValidator extends ConstraintValidator { - public const PATTERN = '/^(?\d{4})-(?\d{2})-(?\d{2})$/'; + public const PATTERN = '/^(?\d{4})-(?\d{2})-(?\d{2})$/D'; /** * Checks whether a date is valid. diff --git a/src/Symfony/Component/Validator/Constraints/EmailValidator.php b/src/Symfony/Component/Validator/Constraints/EmailValidator.php index 8c0ff7730855b..a6ee53f7a7cac 100644 --- a/src/Symfony/Component/Validator/Constraints/EmailValidator.php +++ b/src/Symfony/Component/Validator/Constraints/EmailValidator.php @@ -26,9 +26,9 @@ */ class EmailValidator extends ConstraintValidator { - private const PATTERN_HTML5_ALLOW_NO_TLD = '/^[a-zA-Z0-9.!#$%&\'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/'; - private const PATTERN_HTML5 = '/^[a-zA-Z0-9.!#$%&\'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$/'; - private const PATTERN_LOOSE = '/^.+\@\S+\.\S+$/'; + private const PATTERN_HTML5_ALLOW_NO_TLD = '/^[a-zA-Z0-9.!#$%&\'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/D'; + private const PATTERN_HTML5 = '/^[a-zA-Z0-9.!#$%&\'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$/D'; + private const PATTERN_LOOSE = '/^.+\@\S+\.\S+$/D'; private const EMAIL_PATTERNS = [ Email::VALIDATION_MODE_LOOSE => self::PATTERN_LOOSE, diff --git a/src/Symfony/Component/Validator/Constraints/IbanValidator.php b/src/Symfony/Component/Validator/Constraints/IbanValidator.php index 890ca5f143131..11619efd8ec7a 100644 --- a/src/Symfony/Component/Validator/Constraints/IbanValidator.php +++ b/src/Symfony/Component/Validator/Constraints/IbanValidator.php @@ -73,6 +73,7 @@ class IbanValidator extends ConstraintValidator 'EG' => 'EG\d{2}\d{4}\d{4}\d{17}', // Egypt 'ES' => 'ES\d{2}\d{4}\d{4}\d{1}\d{1}\d{10}', // Spain 'FI' => 'FI\d{2}\d{3}\d{11}', // Finland + 'FK' => 'FK\d{2}[A-Z]{2}\d{12}', // Falkland Islands 'FO' => 'FO\d{2}\d{4}\d{9}\d{1}', // Faroe Islands 'FR' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France 'GA' => 'GA\d{2}\d{23}', // Gabon @@ -117,6 +118,7 @@ class IbanValidator extends ConstraintValidator 'MG' => 'MG\d{2}\d{23}', // Madagascar 'MK' => 'MK\d{2}\d{3}[\dA-Z]{10}\d{2}', // Macedonia 'ML' => 'ML\d{2}[\dA-Z]{2}\d{22}', // Mali + 'MN' => 'MN\d{2}\d{4}\d{12}', // Mongolia 'MQ' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France 'MR' => 'MR\d{2}\d{5}\d{5}\d{11}\d{2}', // Mauritania 'MT' => 'MT\d{2}[A-Z]{4}\d{5}[\dA-Z]{18}', // Malta @@ -127,6 +129,7 @@ class IbanValidator extends ConstraintValidator 'NI' => 'NI\d{2}[A-Z]{4}\d{24}', // Nicaragua 'NL' => 'NL\d{2}[A-Z]{4}\d{10}', // Netherlands (The) 'NO' => 'NO\d{2}\d{4}\d{6}\d{1}', // Norway + 'OM' => 'OM\d{2}\d{3}[\dA-Z]{16}', // Oman 'PF' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France 'PK' => 'PK\d{2}[A-Z]{4}[\dA-Z]{16}', // Pakistan 'PL' => 'PL\d{2}\d{8}\d{16}', // Poland @@ -160,6 +163,7 @@ class IbanValidator extends ConstraintValidator 'VG' => 'VG\d{2}[A-Z]{4}\d{16}', // Virgin Islands 'WF' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France 'XK' => 'XK\d{2}\d{4}\d{10}\d{2}', // Kosovo + 'YE' => 'YE\d{2}[A-Z]{4}\d{4}[\dA-Z]{18}', // Yemen 'YT' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France ]; diff --git a/src/Symfony/Component/Validator/Constraints/TimeValidator.php b/src/Symfony/Component/Validator/Constraints/TimeValidator.php index 473c9e8e01ef2..ef422cdf5a285 100644 --- a/src/Symfony/Component/Validator/Constraints/TimeValidator.php +++ b/src/Symfony/Component/Validator/Constraints/TimeValidator.php @@ -21,8 +21,8 @@ */ class TimeValidator extends ConstraintValidator { - public const PATTERN = '/^(\d{2}):(\d{2}):(\d{2})$/'; - public const PATTERN_WITHOUT_SECONDS = '/^(\d{2}):(\d{2})$/'; + public const PATTERN = '/^(\d{2}):(\d{2}):(\d{2})$/D'; + public const PATTERN_WITHOUT_SECONDS = '/^(\d{2}):(\d{2})$/D'; /** * Checks whether a time is valid. diff --git a/src/Symfony/Component/Validator/Constraints/UrlValidator.php b/src/Symfony/Component/Validator/Constraints/UrlValidator.php index 4a73040a24e23..09173835d6926 100644 --- a/src/Symfony/Component/Validator/Constraints/UrlValidator.php +++ b/src/Symfony/Component/Validator/Constraints/UrlValidator.php @@ -40,10 +40,10 @@ class UrlValidator extends ConstraintValidator \] # an IPv6 address ) (:[0-9]+)? # a port (optional) - (?:/ (?:[\pL\pN\-._\~!$&\'()*+,;=:@]|%%[0-9A-Fa-f]{2})* )* # a path + (?:/ (?:[\pL\pN\pS\pM\-._\~!$&\'()*+,;=:@]|%%[0-9A-Fa-f]{2})* )* # a path (?:\? (?:[\pL\pN\-._\~!$&\'\[\]()*+,;=:@/?]|%%[0-9A-Fa-f]{2})* )? # a query (optional) (?:\# (?:[\pL\pN\-._\~!$&\'()*+,;=:@/?]|%%[0-9A-Fa-f]{2})* )? # a fragment (optional) - $~ixu'; + $~ixuD'; /** * @return void diff --git a/src/Symfony/Component/Validator/Resources/bin/sync-iban-formats.php b/src/Symfony/Component/Validator/Resources/bin/sync-iban-formats.php index 4042bddf266c3..f41373cf2112a 100755 --- a/src/Symfony/Component/Validator/Resources/bin/sync-iban-formats.php +++ b/src/Symfony/Component/Validator/Resources/bin/sync-iban-formats.php @@ -168,7 +168,7 @@ public function getIbanFormats(): array $formats = []; foreach ($this->readIbanFormatsTable() as $item) { - if (!preg_match('/^([A-Z]{2})/', $item['Example'], $matches)) { + if (!preg_match('/^([A-Z]{2})/', $item['IBAN Fields'], $matches)) { continue; } diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.af.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.af.xlf index e09d3fc06aa70..706f0ca49716b 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.af.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.af.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.ar.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.ar.xlf index 94a91d42a71bb..6c684d98df31b 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.ar.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.ar.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.az.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.az.xlf index 390e5f869c323..0b149024ca2dd 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.az.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.az.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.be.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.be.xlf index 3ebae4cb6bb2f..3db0ddc20f3d5 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.be.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.be.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.bg.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.bg.xlf index dffefdb7d9056..e0792e209561f 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.bg.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.bg.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.bs.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.bs.xlf index f5e90aba54a7a..150025d03a6ac 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.bs.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.bs.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.ca.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.ca.xlf index 60f747f62f715..fb3c41dbc747b 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.ca.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.ca.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. Aquest valor és massa llarg. Ha de contenir una paraula.|Aquest valor és massa llarg. Ha de contenir {{ max }} paraules o menys. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.cs.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.cs.xlf index 459d07fd727cc..e99d3236eff40 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.cs.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.cs.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.cy.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.cy.xlf index 7f3357daf5398..667f4a6d453d0 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.cy.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.cy.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.da.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.da.xlf index d80251b2a7483..5d08a01df77b1 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.da.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.da.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.de.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.de.xlf index 6a9919ddd36ad..301ee496e68e6 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.de.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.de.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. Dieser Wert ist zu lang. Er darf maximal aus einem Wort bestehen.|Dieser Wert ist zu lang. Er darf maximal {{ max }} Wörter enthalten. + + This value does not represent a valid week in the ISO 8601 format. + Dieser Wert ist keine Wochenangabe im ISO 8601-Format. + + + This value is not a valid week. + Dieser Wert ist keine gültige Woche. + + + This value should not be before week "{{ min }}". + Dieser Wert darf nicht vor der Woche "{{ min }}" sein. + + + This value should not be after week "{{ max }}". + Dieser Wert darf nicht nach der Woche "{{ max }}" sein. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.el.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.el.xlf index bb0ccb46e92ec..e58dd3d77e7fe 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.el.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.el.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf index cf08ea281938f..faf549e483512 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf index f9b3277229c8a..fa26c72100068 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. Este valor es demasiado largo. Debe contener una palabra.|Este valor es demasiado largo. Debe contener {{ max }} palabras o menos. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.et.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.et.xlf index 988bb0aa07203..774445dd02c62 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.et.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.et.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.eu.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.eu.xlf index 362dfa9c0cd34..3e1a544c89053 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.eu.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.eu.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.fa.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.fa.xlf index fb8b629b4d1a3..98486482b239a 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.fa.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.fa.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.fi.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.fi.xlf index 6b8902f014dc2..2dac5b5b8af24 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.fi.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.fi.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf index f06189712e3a0..2fb4eeac18725 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. Cette valeur est trop longue. Elle doit contenir au maximum un mot.|Cette valeur est trop longue. Elle doit contenir au maximum {{ max }} mots. + + This value does not represent a valid week in the ISO 8601 format. + Cette valeur ne représente pas une semaine valide au format ISO 8601. + + + This value is not a valid week. + Cette valeur n'est pas une semaine valide. + + + This value should not be before week "{{ min }}". + Cette valeur ne doit pas être antérieure à la semaine "{{ min }}". + + + This value should not be after week "{{ max }}". + Cette valeur ne doit pas être postérieure à la semaine "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.gl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.gl.xlf index 7885473fb2e84..1a48093dca758 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.gl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.gl.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.he.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.he.xlf index 6e5ab52297777..73ccca53f2acd 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.he.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.he.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.hr.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.hr.xlf index 0ddbeb6f20c81..147f4313c8a5e 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.hr.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.hr.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.hu.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.hu.xlf index 0c8002ae1ecc4..185ebf02b57ee 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.hu.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.hu.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.hy.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.hy.xlf index 29f916fff06d1..24423b0822e68 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.hy.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.hy.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.id.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.id.xlf index 2814599a0fc64..3bffae84d63c7 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.id.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.id.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.it.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.it.xlf index 1f409315e6dbf..1e77aba17aa79 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.it.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.it.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.ja.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.ja.xlf index d94a414e31998..26cb6e5933f04 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.ja.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.ja.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.lb.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.lb.xlf index 3c0a6f200e4f8..8b0b6a244dcff 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.lb.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.lb.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.lt.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.lt.xlf index dc28eeba77223..e30f8a6ae3e40 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.lt.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.lt.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. Per didelis žodžių skaičius. Turi susidaryti iš 1 žodžio.|Per didelis žodžių skaičius. Turi susidaryti iš {{ max }} arba mažiau žodžių. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.lv.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.lv.xlf index 9b2b9bd9f4485..fef1c3662df5f 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.lv.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.lv.xlf @@ -48,7 +48,7 @@ This value is not a valid datetime. - Šī vērtība ir nederīgs datums un laiks + Šī vērtība ir nederīgs datums un laiks. This value is not a valid email address. @@ -444,11 +444,27 @@ This value is too short. It should contain at least one word.|This value is too short. It should contain at least {{ min }} words. - This value is too short. It should contain at least one word.|This value is too short. It should contain at least {{ min }} words. + Šī vērtība ir pārāk īsa. Tai būtu jābūt vismaz vienu vārdu garai.|Šī vērtība ir pārāk īsa. Tai būtu jābūt ne mazāk kā {{ min }} vārdus garai. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. - This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + Šī vērtība ir pārāk gara. Tai būtu jābūt vienam vārdam.|Šī vērtība ir pārāk gara. Tai būtu jābūt ne vairāk kā {{ max }} vārdus garai. + + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.mk.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.mk.xlf index b891990799cd3..722c9a7893844 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.mk.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.mk.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.mn.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.mn.xlf index 987d73199ac09..0c9f8c84d0d3c 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.mn.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.mn.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.my.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.my.xlf index b7353e83a4c7d..89bb0906ec187 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.my.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.my.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.nb.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.nb.xlf index 2abe0fb7f0805..d0a0e6509df15 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.nb.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.nb.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf index 96e1d20d93d0f..fdea10f0e4a80 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf @@ -444,11 +444,27 @@ This value is too short. It should contain at least one word.|This value is too short. It should contain at least {{ min }} words. - This value is too short. It should contain at least one word.|This value is too short. It should contain at least {{ min }} words. + Deze waarde is te kort. Het moet ten minste één woord bevatten.|Deze waarde is te kort. Het moet ten minste {{ min }} woorden bevatten. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. - This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + Deze waarde is te lang. Het moet één woord zijn.|Deze waarde is te lang. Het mag maximaal {{ max }} woorden bevatten. + + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.nn.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.nn.xlf index e825815ced1b6..8ff78c5a08132 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.nn.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.nn.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.no.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.no.xlf index 2abe0fb7f0805..d0a0e6509df15 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.no.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.no.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.pl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.pl.xlf index 337a5949501ce..541a35d73a83a 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.pl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.pl.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. Podana wartość jest zbyt długa. Powinna zawierać jedno słowo.|Podana wartość jest zbyt długa. Powinna zawierać {{ max }} słów lub mniej. + + This value does not represent a valid week in the ISO 8601 format. + Podana wartość nie jest poprawnym oznaczeniem tygodnia w formacie ISO 8601. + + + This value is not a valid week. + Podana wartość nie jest poprawnym oznaczeniem tygodnia. + + + This value should not be before week "{{ min }}". + Podana wartość nie powinna być przed tygodniem "{{ min }}". + + + This value should not be after week "{{ max }}". + Podana wartość nie powinna być po tygodniu "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.pt.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.pt.xlf index f771faa84f5de..bb3208cfa5190 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.pt.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.pt.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.pt_BR.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.pt_BR.xlf index e600bb17ff7f6..c427f95d3e670 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.pt_BR.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.pt_BR.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.ro.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.ro.xlf index 79cf6941acc57..7413619650d94 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.ro.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.ro.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.ru.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.ru.xlf index 70cb1144bf899..e8dd0311640ff 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.ru.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.ru.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.sk.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.sk.xlf index 8785adcc18257..aeda9c94b6b4c 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.sk.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.sk.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.sl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.sl.xlf index 4926c1b4f815e..1a8cb8d57bbaa 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.sl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.sl.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.sq.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.sq.xlf index 9942b5cf26bc6..debbe5feb9eb6 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.sq.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.sq.xlf @@ -459,6 +459,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.sr_Cyrl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.sr_Cyrl.xlf index 3aa3be49e8d45..2e601246e3e01 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.sr_Cyrl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.sr_Cyrl.xlf @@ -136,7 +136,7 @@ This value is not a valid IP address. - Ова вредност није валидна IP адреса. + Ова вредност није валидна IP адреса. This value is not a valid language. @@ -192,7 +192,7 @@ No temporary folder was configured in php.ini, or the configured folder does not exist. - Привремени директоријум није конфигурисан у php.ini, или конфигурисани директоријум не постоји. + Привремени директоријум није конфигурисан у php.ini, или конфигурисани директоријум не постоји. Cannot write temporary file to disk. @@ -224,7 +224,7 @@ This value is not a valid International Bank Account Number (IBAN). - Ова вредност није валидан Међународни број банкарског рачуна (IBAN). + Ова вредност није валидан Међународни број банковног рачуна (IBAN). This value is not a valid ISBN-10. @@ -312,7 +312,7 @@ This value is not a valid Business Identifier Code (BIC). - Ова вредност није валидан Код за идентификацију бизниса (BIC). + Ова вредност није валидна Код за идентификацију бизниса (BIC). Error @@ -320,7 +320,7 @@ This value is not a valid UUID. - Ова вредност није валидан UUID. + Ова вредност није валидан UUID. This value should be a multiple of {{ compared_value }}. @@ -440,15 +440,31 @@ This URL is missing a top-level domain. - Овом URL недостаје домен највишег нивоа. + Овом УРЛ-у недостаје домен највишег нивоа. This value is too short. It should contain at least one word.|This value is too short. It should contain at least {{ min }} words. - This value is too short. It should contain at least one word.|This value is too short. It should contain at least {{ min }} words. + Ова вредност је прекратка. Треба да садржи макар једну реч.|Ова вредност је прекратка. Треба да садржи макар {{ min }} речи. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. - This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + Ова вредност је предугачка. Треба да садржи само једну реч.|Ова вредност је предугачка. Треба да садржи највише {{ max }} речи. + + + This value does not represent a valid week in the ISO 8601 format. + Ова вредност не представља валидну недељу у ISO 8601 формату. + + + This value is not a valid week. + Ова вредност није валидна недеља. + + + This value should not be before week "{{ min }}". + Ова вредност не би требала да буде пре недеље "{{ min }}". + + + This value should not be after week "{{ max }}". + Ова вредност не би требала да буде после недеље "{{ max }}". diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.sr_Latn.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.sr_Latn.xlf index ac7d7186dfee7..8e27e114c85fa 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.sr_Latn.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.sr_Latn.xlf @@ -136,7 +136,7 @@ This value is not a valid IP address. - Ova vrednost nije validna IP adresa. + Ova vrednost nije validna IP adresa. This value is not a valid language. @@ -192,7 +192,7 @@ No temporary folder was configured in php.ini, or the configured folder does not exist. - Privremeni direktorijum nije konfigurisan u php.ini, ili konfigurisani direktorijum ne postoji. + Privremeni direktorijum nije konfigurisan u php.ini, ili direktorijum koji je konfigurisan ne postoji. Cannot write temporary file to disk. @@ -224,7 +224,7 @@ This value is not a valid International Bank Account Number (IBAN). - Ova vrednost nije validan Međunarodni broj bankovnog računa (IBAN). + Ova vrednost nije validan Međunarodni broj bankovnog računa (IBAN). This value is not a valid ISBN-10. @@ -312,7 +312,7 @@ This value is not a valid Business Identifier Code (BIC). - Ova vrednost nije validan Kod za identifikaciju biznisa (BIC). + Ova vrednost nije validan Kod za identifikaciju biznisa (BIC). Error @@ -320,7 +320,7 @@ This value is not a valid UUID. - Ova vrednost nije validan UUID. + Ova vrednost nije validan UUID. This value should be a multiple of {{ compared_value }}. @@ -436,19 +436,35 @@ This value is not a valid MAC address. - Ova vrednost nije validna MAC adresa. + Ova vrednost nije validna MAC adresa. This URL is missing a top-level domain. - Ovom URL nedostaje domen najvišeg nivoa. + Ovom URL nedostaje domen najvišeg nivoa. This value is too short. It should contain at least one word.|This value is too short. It should contain at least {{ min }} words. - This value is too short. It should contain at least one word.|This value is too short. It should contain at least {{ min }} words. + Ova vrednost je prekratka. Treba da sadrži makar jednu reč.|Ova vrednost je prekratka. Treba da sadrži makar {{ min }} reči. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. - This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + Ova vrednost je predugačka. Treba da sadrži samo jednu reč.|Ova vrednost je predugačka. Treba da sadrži najviše {{ max }} reči. + + + This value does not represent a valid week in the ISO 8601 format. + Ova vrednost ne predstavlja validnu nedelju u ISO 8601 formatu. + + + This value is not a valid week. + Ova vrednost nije validna nedelja + + + This value should not be before week "{{ min }}". + Ova vrednost ne bi trebala da bude pre nedelje "{{ min }}". + + + This value should not be after week "{{ max }}". + Ova vrednost ne bi trebala da bude posle nedelje "{{ max }}". diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.sv.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.sv.xlf index 01668a87d21b3..ac08eff2a931e 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.sv.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.sv.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.th.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.th.xlf index c6f0b829a6af6..ded3a00868551 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.th.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.th.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.tl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.tl.xlf index 1d831bd8ea0f3..4ac6bb45699ff 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.tl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.tl.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.tr.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.tr.xlf index 685e6ca1a928d..af59485b35d45 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.tr.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.tr.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.uk.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.uk.xlf index b67e3e604decc..4775d04f44957 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.uk.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.uk.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.ur.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.ur.xlf index d18604407c71c..a1669de019a0a 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.ur.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.ur.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.uz.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.uz.xlf index d21bc24a3cc5b..d3012c64ef967 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.uz.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.uz.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.vi.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.vi.xlf index e1cdb6d09fb91..70a7eedcf24e5 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.vi.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.vi.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.zh_CN.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.zh_CN.xlf index 15b234fb0d4ef..3c078d3f5816c 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.zh_CN.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.zh_CN.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.zh_TW.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.zh_TW.xlf index 3812029fcad81..8c7caa5236713 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.zh_TW.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.zh_TW.xlf @@ -450,6 +450,22 @@ This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + + This value does not represent a valid week in the ISO 8601 format. + This value does not represent a valid week in the ISO 8601 format. + + + This value is not a valid week. + This value is not a valid week. + + + This value should not be before week "{{ min }}". + This value should not be before week "{{ min }}". + + + This value should not be after week "{{ max }}". + This value should not be after week "{{ max }}". + diff --git a/src/Symfony/Component/Validator/Tests/Constraints/AtLeastOneOfValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/AtLeastOneOfValidatorTest.php index 9e2da70f7f00f..1c0c650b9a767 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/AtLeastOneOfValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/AtLeastOneOfValidatorTest.php @@ -14,6 +14,7 @@ use Symfony\Component\Validator\Constraints\AtLeastOneOf; use Symfony\Component\Validator\Constraints\AtLeastOneOfValidator; use Symfony\Component\Validator\Constraints\Choice; +use Symfony\Component\Validator\Constraints\Collection; use Symfony\Component\Validator\Constraints\Count; use Symfony\Component\Validator\Constraints\Country; use Symfony\Component\Validator\Constraints\DivisibleBy; @@ -27,9 +28,11 @@ use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\LessThan; use Symfony\Component\Validator\Constraints\Negative; +use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\NotNull; use Symfony\Component\Validator\Constraints\Range; use Symfony\Component\Validator\Constraints\Regex; +use Symfony\Component\Validator\Constraints\Type; use Symfony\Component\Validator\Constraints\Unique; use Symfony\Component\Validator\Constraints\Valid; use Symfony\Component\Validator\ConstraintViolation; @@ -296,6 +299,35 @@ public function trans(?string $id, array $parameters = [], ?string $domain = nul $this->assertCount(1, $violations); $this->assertSame('Dummy translation: [1] Dummy violation.', $violations->get(0)->getMessage()); } + + public function testValidateNestedAtLeaseOneOfConstraints() + { + $data = [ + 'foo' => [ + 'bar' => 'foo.bar', + 'baz' => 'foo.baz', + ], + ]; + + $constraints = new Collection([ + 'foo' => new AtLeastOneOf([ + new Collection([ + 'bar' => new AtLeastOneOf([ + new Type('int'), + new Choice(['test1', 'test2']) + ]), + ]), + new Collection([ + 'baz' => new Type('int'), + ]), + ]), + ]); + + $validator = Validation::createValidator(); + $violations = $validator->validate($data, $constraints); + + self::assertCount(1, $violations); + } } class ExpressionConstraintNested diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CardSchemeValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CardSchemeValidatorTest.php index 7b28ca9dd15d2..15f4fa63452dc 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/CardSchemeValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/CardSchemeValidatorTest.php @@ -46,6 +46,19 @@ public function testValidNumbers($scheme, $number) $this->assertNoViolation(); } + /** + * @dataProvider getValidNumbers + */ + public function testValidNumbersWithNewLine($scheme, $number) + { + $this->validator->validate($number."\n", new CardScheme(['schemes' => $scheme, 'message' => 'myMessage'])); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$number."\n\"") + ->setCode(CardScheme::INVALID_FORMAT_ERROR) + ->assertRaised(); + } + public function testValidNumberWithOrderedArguments() { $this->validator->validate( diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CssColorValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CssColorValidatorTest.php index 6c298f8236791..ce121977c0924 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/CssColorValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/CssColorValidatorTest.php @@ -52,6 +52,19 @@ public function testValidAnyColor($cssColor) $this->assertNoViolation(); } + /** + * @dataProvider getValidAnyColor + */ + public function testValidAnyColorWithNewLine($cssColor) + { + $this->validator->validate($cssColor."\n", new CssColor([], 'myMessage')); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$cssColor."\n\"") + ->setCode(CssColor::INVALID_FORMAT_ERROR) + ->assertRaised(); + } + public static function getValidAnyColor(): array { return [ diff --git a/src/Symfony/Component/Validator/Tests/Constraints/DateValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/DateValidatorTest.php index e8642f04a8ec1..93dab41f24622 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/DateValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/DateValidatorTest.php @@ -53,6 +53,19 @@ public function testValidDates($date) $this->assertNoViolation(); } + /** + * @dataProvider getValidDates + */ + public function testValidDatesWithNewLine(string $date) + { + $this->validator->validate($date."\n", new Date(['message' => 'myMessage'])); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$date."\n\"") + ->setCode(Date::INVALID_FORMAT_ERROR) + ->assertRaised(); + } + public static function getValidDates() { return [ diff --git a/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php index 6bf8fec6c717a..565af9561bfce 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php @@ -73,6 +73,19 @@ public function testValidEmails($email) $this->assertNoViolation(); } + /** + * @dataProvider getValidEmails + */ + public function testValidEmailsWithNewLine($email) + { + $this->validator->validate($email."\n", new Email()); + + $this->buildViolation('This value is not a valid email address.') + ->setParameter('{{ value }}', '"'.$email."\n\"") + ->setCode(Email::INVALID_FORMAT_ERROR) + ->assertRaised(); + } + public static function getValidEmails() { return [ diff --git a/src/Symfony/Component/Validator/Tests/Constraints/IbanValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/IbanValidatorTest.php index 886a313b83b5e..a9f59b09c63ac 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/IbanValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/IbanValidatorTest.php @@ -48,6 +48,19 @@ public function testValidIbans($iban) $this->assertNoViolation(); } + /** + * @dataProvider getValidIbans + */ + public function testValidIbansWithNewLine(string $iban) + { + $this->validator->validate($iban."\n", new Iban()); + + $this->buildViolation('This is not a valid International Bank Account Number (IBAN).') + ->setParameter('{{ value }}', '"'.$iban."\n\"") + ->setCode(Iban::INVALID_CHARACTERS_ERROR) + ->assertRaised(); + } + public static function getValidIbans() { return [ @@ -73,6 +86,7 @@ public static function getValidIbans() ['CZ65 0800 0000 1920 0014 5399'], // Czech Republic ['DK50 0040 0440 1162 43'], // Denmark ['EE38 2200 2210 2014 5685'], // Estonia + ['FK12 SC98 7654 3210 98'], // Falkland Islands ['FO97 5432 0388 8999 44'], // Faroe Islands ['FI21 1234 5600 0007 85'], // Finland ['FR14 2004 1010 0505 0001 3M02 606'], // France @@ -96,9 +110,11 @@ public static function getValidIbans() ['MU17 BOMM 0101 1010 3030 0200 000M UR'], // Mauritius ['MD24 AG00 0225 1000 1310 4168'], // Moldova ['MC93 2005 2222 1001 1223 3M44 555'], // Monaco + ['MN14 0005 0051 6384 7716'], // Mongolia ['ME25 5050 0001 2345 6789 51'], // Montenegro ['NL39 RABO 0300 0652 64'], // Netherlands ['NO93 8601 1117 947'], // Norway + ['OM04 0280 0000 1234 5678 901'], // Oman ['PK36 SCBL 0000 0011 2345 6702'], // Pakistan ['PL60 1020 1026 0000 0422 7020 1111'], // Poland ['PT50 0002 0123 1234 5678 9015 4'], // Portugal @@ -115,6 +131,7 @@ public static function getValidIbans() ['TR33 0006 1005 1978 6457 8413 26'], // Turkey ['AE07 0331 2345 6789 0123 456'], // UAE ['GB12 CPBK 0892 9965 0449 91'], // United Kingdom + ['YE09 CBKU 0000 0000 0000 1234 5601 01'], // Yemen ['DJ21 0001 0000 0001 5400 0100 186'], // Djibouti ['EG38 0019 0005 0000 0000 2631 8000 2'], // Egypt diff --git a/src/Symfony/Component/Validator/Tests/Constraints/TimeValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/TimeValidatorTest.php index 91d6778ed4ec1..5d9027a17086f 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/TimeValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/TimeValidatorTest.php @@ -60,6 +60,19 @@ public function testValidTimes($time) $this->assertNoViolation(); } + /** + * @dataProvider getValidTimes + */ + public function testValidTimesWithNewLine(string $time) + { + $this->validator->validate($time."\n", new Time()); + + $this->buildViolation('This value is not a valid time.') + ->setParameter('{{ value }}', '"'.$time."\n".'"') + ->setCode(Time::INVALID_FORMAT_ERROR) + ->assertRaised(); + } + public static function getValidTimes() { return [ @@ -81,6 +94,19 @@ public function testValidTimesWithoutSeconds(string $time) $this->assertNoViolation(); } + /** + * @dataProvider getValidTimesWithoutSeconds + */ + public function testValidTimesWithoutSecondsWithNewLine(string $time) + { + $this->validator->validate($time."\n", new Time(withSeconds: false)); + + $this->buildViolation('This value is not a valid time.') + ->setParameter('{{ value }}', '"'.$time."\n".'"') + ->setCode(Time::INVALID_FORMAT_ERROR) + ->assertRaised(); + } + public static function getValidTimesWithoutSeconds() { return [ diff --git a/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php index 18d6e9a49a384..e2ffcb4ae130f 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php @@ -60,6 +60,19 @@ public function testValidUrls($url) $this->assertNoViolation(); } + /** + * @dataProvider getValidUrls + */ + public function testValidUrlsWithNewLine($url) + { + $this->validator->validate($url."\n", new Url()); + + $this->buildViolation('This value is not a valid URL.') + ->setParameter('{{ value }}', '"'.$url."\n".'"') + ->setCode(Url::INVALID_URL_ERROR) + ->assertRaised(); + } + /** * @dataProvider getValidUrlsWithWhitespaces */ @@ -85,6 +98,24 @@ public function testValidRelativeUrl($url) $this->assertNoViolation(); } + /** + * @dataProvider getValidRelativeUrls + * @dataProvider getValidUrls + */ + public function testValidRelativeUrlWithNewLine(string $url) + { + $constraint = new Url([ + 'relativeProtocol' => true, + ]); + + $this->validator->validate($url."\n", $constraint); + + $this->buildViolation('This value is not a valid URL.') + ->setParameter('{{ value }}', '"'.$url."\n".'"') + ->setCode(Url::INVALID_URL_ERROR) + ->assertRaised(); + } + public static function getValidRelativeUrls() { return [ @@ -174,6 +205,8 @@ public static function getValidUrls() ['http://symfony.com/#one_more%20test'], ['http://example.com/exploit.html?hello[0]=test'], ['http://বিডিআইএ.বাংলা'], + ['http://www.example.com/คนแซ่ลี้/'], + ['http://www.example.com/か/'], ]; } diff --git a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php index e0b17eecafdc9..e36cee6a9bb53 100644 --- a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php @@ -621,7 +621,7 @@ private function hasColorSupport(mixed $stream): bool } // Follow https://no-color.org/ - if ('' !== ($_SERVER['NO_COLOR'] ?? getenv('NO_COLOR') ?: '')) { + if ('' !== (($_SERVER['NO_COLOR'] ?? getenv('NO_COLOR'))[0] ?? '')) { return false; } diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php index 70bacf796214f..653e7a5fd1388 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php @@ -555,11 +555,11 @@ public function testGenerator() function: "Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::baz" this: Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo { …} %s: { - %sGeneratorDemo.php:14 { + %sGeneratorDemo.php:12 { Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo->baz() + › + › public function baz() › { - › yield from bar(); - › } } Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo->baz() {} %A} @@ -580,7 +580,9 @@ function: "Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::baz" %s: { %s%eTests%eFixtures%eGeneratorDemo.php:%d { Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::foo() -%A › yield 1; + › { + › yield 1; + › } %A } %s%eTests%eFixtures%eGeneratorDemo.php:20 { …} %s%eTests%eFixtures%eGeneratorDemo.php:14 { …} @@ -592,9 +594,9 @@ function: "Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::foo" %s: { %s%eTests%eFixtures%eGeneratorDemo.php:%d { Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::foo() + › { › yield 1; › } - › } %A } closed: false diff --git a/src/Symfony/Component/VarDumper/Tests/Dumper/CliDumperTest.php b/src/Symfony/Component/VarDumper/Tests/Dumper/CliDumperTest.php index 824c9f10df606..6e55bc4c4cc18 100644 --- a/src/Symfony/Component/VarDumper/Tests/Dumper/CliDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Dumper/CliDumperTest.php @@ -343,7 +343,7 @@ public function testThrowingCaster() #message: "Unexpected Exception thrown from a caster: Foobar" trace: { %sTwig.php:2 { - __TwigTemplate_VarDumperFixture_u75a09->doDisplay(array \$context, array \$blocks = []) + __TwigTemplate_VarDumperFixture_u75a09->doDisplay(array \$context, array \$blocks = []): array › foo bar › twig source › diff --git a/src/Symfony/Component/VarDumper/Tests/Fixtures/Twig.php b/src/Symfony/Component/VarDumper/Tests/Fixtures/Twig.php index bb6e578d5015c..e26a3925490ac 100644 --- a/src/Symfony/Component/VarDumper/Tests/Fixtures/Twig.php +++ b/src/Symfony/Component/VarDumper/Tests/Fixtures/Twig.php @@ -28,23 +28,23 @@ public function __construct(?Twig\Environment $env = null, $path = null) $this->path = $path; } - protected function doDisplay(array $context, array $blocks = []) + protected function doDisplay(array $context, array $blocks = []): array { // line 2 throw new \Exception('Foobar'); } - public function getTemplateName() + public function getTemplateName(): string { return 'foo.twig'; } - public function getDebugInfo() + public function getDebugInfo(): array { return [33 => 1, 34 => 2]; } - public function getSourceContext() + public function getSourceContext(): Twig\Source { return new Twig\Source(" foo bar\n twig source\n\n", 'foo.twig', $this->path ?: __FILE__); } diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index 1ba90dc51b328..14f95d6244132 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -709,8 +709,13 @@ private static function evaluateScalar(string $scalar, int $flags, array &$refer case Parser::preg_match('/^(-|\+)?[0-9][0-9_]*(\.[0-9_]+)?$/', $scalar): return (float) str_replace('_', '', $scalar); case Parser::preg_match(self::getTimestampRegex(), $scalar): - // When no timezone is provided in the parsed date, YAML spec says we must assume UTC. - $time = new \DateTimeImmutable($scalar, new \DateTimeZone('UTC')); + try { + // When no timezone is provided in the parsed date, YAML spec says we must assume UTC. + $time = new \DateTimeImmutable($scalar, new \DateTimeZone('UTC')); + } catch (\Exception $e) { + // Some dates accepted by the regex are not valid dates. + throw new ParseException(\sprintf('The date "%s" could not be parsed as it is an invalid date.', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename, $e); + } if (Yaml::PARSE_DATETIME & $flags) { return $time; diff --git a/src/Symfony/Component/Yaml/Tests/InlineTest.php b/src/Symfony/Component/Yaml/Tests/InlineTest.php index 0573a990d4805..f7a51b9e69138 100644 --- a/src/Symfony/Component/Yaml/Tests/InlineTest.php +++ b/src/Symfony/Component/Yaml/Tests/InlineTest.php @@ -613,6 +613,14 @@ public function testParseNestedTimestampListAsDateTimeObject(string $yaml, int $ $this->assertEquals($expectedNested, Inline::parse($yamlNested, Yaml::PARSE_DATETIME)); } + public function testParseInvalidDate() + { + $this->expectException(ParseException::class); + $this->expectExceptionMessageMatches('/^The date "2024-50-50" could not be parsed as it is an invalid date.*/'); + + Inline::parse('2024-50-50', Yaml::PARSE_DATETIME); + } + /** * @dataProvider getDateTimeDumpTests */