diff --git a/.github/ISSUE_TEMPLATE/1_Bug_report.yaml b/.github/ISSUE_TEMPLATE/1_Bug_report.yaml new file mode 100644 index 0000000000000..5518d4e4ad79d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/1_Bug_report.yaml @@ -0,0 +1,38 @@ +name: 🐛 Bug Report +description: ⚠️ See below for security reports +labels: Bug + +body: + - type: input + id: affected-versions + attributes: + label: Symfony version(s) affected + placeholder: x.y.z + validations: + required: true + - type: textarea + id: description + attributes: + label: Description + description: A clear and consise description of the problem + validations: + required: true + - type: textarea + id: how-to-reproduce + attributes: + label: How to reproduce + description: | + Code and/or config needed to reproduce the problem. + If it's a complex bug, create a "bug reproducer" as explained in https://symfony.com/doc/current/contributing/code/reproducer.html + validations: + required: true + - type: textarea + id: possible-solution + attributes: + label: Possible Solution + description: "Optional: only if you have suggestions on a fix/reason for the bug" + - type: textarea + id: additional-context + attributes: + label: Additional Context + description: "Optional: any other context about the problem: log messages, screenshots, etc." diff --git a/.github/ISSUE_TEMPLATE/2_Feature_request.yaml b/.github/ISSUE_TEMPLATE/2_Feature_request.yaml new file mode 100644 index 0000000000000..bd300eb1e82b2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/2_Feature_request.yaml @@ -0,0 +1,17 @@ +name: 🚀 Feature Request +description: RFC and ideas for new features and improvements +body: + - type: textarea + id: description + attributes: + label: Description + description: A clear and concise description of the new feature + validations: + required: true + - type: textarea + id: example + attributes: + label: Example + description: | + A simple example of the new feature in action (include PHP code, YAML config, etc.) + If the new feature changes an existing feature, include a simple before/after comparison. diff --git a/CHANGELOG-5.3.md b/CHANGELOG-5.3.md index 3cd96283ed8b6..dff9252ddcf24 100644 --- a/CHANGELOG-5.3.md +++ b/CHANGELOG-5.3.md @@ -7,6 +7,46 @@ in 5.3 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/v5.3.0...v5.3.1 +* 5.3.10 (2021-10-29) + + * bug #43798 [Dotenv] Duplicate $_SERVER values in $_ENV if they don't exist (fancyweb) + * bug #43799 [PhpUnitBridge] fix symlink to bridge in docker by making its path relative (nicolas-grekas) + * bug #43801 [TwigBundle] fix auto-enabling assets/expression/routing/yaml/workflow extensions (nicolas-grekas) + * bug #43790 [String] Fix inflector for "zombies" (acodispo) + * bug #43781 [Messenger] Fix `TraceableMessageBus` implementation so it can compute caller even when used within a callback (Ocramius) + * bug #43589 [Lock] Fix incorrect return type in PostgreSqlStore (GromNaN) + * bug #43627 [Framework][Secrets] Fix service definition when local vault is disabled (GromNaN) + * bug #43579 [DependencyInjection] Fix autowiring tagged arguments from attributes (Okhoshi) + * bug #43655 [VarDumper] Fix dumping twig templates found in exceptions (event15) + * bug #43484 [Messenger] Fix Redis Transport when username is empty (villfa) + * bug #43659 Fix logging of impersonator introduced in 5.3 (johanwilfer) + * bug #43630 [Messenger] Fix unwrapping the Postgres connection in DBAL 3 (derrabus) + * bug #43568 [Messenger] fix: TypeError in PhpSerializer::encode() (dsech) + * bug #43591 [Config] Fix files sorting in GlobResource (lyrixx) + * bug #43569 [HttpClient] fix collecting debug info on destruction of CurlResponse (nicolas-grekas) + * bug #43545 [DependencyInjection] fix "url" env var processor (nicolas-grekas) + * bug #43537 [HttpClient] fix RetryableHttpClient when a response is canceled (nicolas-grekas) + * bug #43533 [Uid] fix 4 missing bits of entropy in UUIDv4 (nicolas-grekas) + * bug #43376 [Runtime] Fix class validation of composer "extra.runtime.class" (piku235) + * bug #43413 [VarDumper] Fix error with uninitialized XMLReader (villfa) + * bug #43439 [Notifier] [RocketChat] Fix undefined index for message id (OskarStark) + * bug #43408 [Notifier] Fix 'Undefined array key' error in FirebaseTransport (villfa) + * bug #43388 [Validator] Fixes URL validation for single-char subdomains (DfKimera) + * bug #41534 [Form] Fix ChoiceType to effectively set and use translator (marek-binkowski-sim) + * bug #43364 [Translation] Use symfony default locale when pulling translations from providers (Yoann MOROCUTTI) + * bug #43333 [HttpClient] fix missing kernel.reset tag on TraceableHttpClient services (nicolas-grekas) + * bug #43302 [Cache] Commit items implicitly only when deferred keys are requested (Sergey Belyshkin) + * bug #43330 [Cache][Lock] fix SQLSRV throws for method_exists() (GDmac) + * bug #43270 [VarDumper] Fix handling of "new" in initializers on PHP 8.1 (nicolas-grekas) + * bug #43312 [Translation] [Bridge] [Lokalise] do not export empty strings (taranovegor) + * bug #43277 [DependencyInjection] fix support for "new" in initializers on PHP 8.1 (nicolas-grekas) + * bug #43243 [HttpClient] accept headers when CURLE_RECV_ERROR is received before the content (nicolas-grekas) + * bug #43208 [Serializer] Attributes that extend serializer`s annotations are not ignored by the serialization process (Alexander Onatskiy) + * bug #43241 [PhpUnitBridge] Do not override correct triggering file for return type deprecations (wouterj) + * bug #43204 [Serializer] Fix denormalizing XML array with empty body (5.x) (alexandre-daubois) + * bug #43205 [Serializer] Fix denormalizing XML array with empty body (4.4) (alexandre-daubois) + * bug #43235 [Security] Remove annoying deprecation in `UsageTrackingTokenStorage` (chalasr) + * 5.3.9 (2021-09-28) * Fix subtree split issue diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index f804602f54457..507ca7e28d68b 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -7,16 +7,16 @@ The Symfony Connect username in parenthesis allows to get more information - Fabien Potencier (fabpot) - Nicolas Grekas (nicolas-grekas) - Christian Flothmann (xabbuh) - - Bernhard Schussek (bschussek) - Alexander M. Turek (derrabus) + - Bernhard Schussek (bschussek) - Tobias Schultze (tobion) - Robin Chalas (chalas_r) - Christophe Coevoet (stof) - Wouter De Jong (wouterj) - Jérémy DERUSSÉ (jderusse) - Maxime Steinhausser (ogizanagi) - - Kévin Dunglas (dunglas) - Grégoire Pineau (lyrixx) + - Kévin Dunglas (dunglas) - Jordi Boggiano (seldaek) - Victor Berchet (victor) - Javier Eguiluz (javier.eguiluz) @@ -66,8 +66,8 @@ The Symfony Connect username in parenthesis allows to get more information - stealth35 ‏ (stealth35) - Alexander Mols (asm89) - Titouan Galopin (tgalopin) - - Vasilij Dusko | CREATION - Laurent VOULLEMIER (lvo) + - Vasilij Dusko | CREATION - Bulat Shakirzyanov (avalanche123) - David Maicher (dmaicher) - gadelat (gadelat) @@ -81,17 +81,17 @@ The Symfony Connect username in parenthesis allows to get more information - Konstantin Kudryashov (everzet) - Vladimir Reznichenko (kalessil) - Bilal Amarni (bamarni) + - Jérôme Tamarelle (gromnan) - Florin Patan (florinpatan) - Jáchym Toušek (enumag) - - Jérôme Tamarelle (gromnan) - Alex Pott - Michel Weimerskirch (mweimerskirch) - Andrej Hudec (pulzarraider) - Christian Raue - Issei Murasawa (issei_m) + - Antoine M (amakdessi) - Eric Clemmons (ericclemmons) - Charles Sarrazin (csarrazi) - - Antoine M (amakdessi) - Vasilij Dusko - Douglas Greenshields (shieldo) - Graham Campbell (graham) @@ -107,18 +107,18 @@ The Symfony Connect username in parenthesis allows to get more information - Brandon Turner - Luis Cordova (cordoval) - Daniel Holmes (dholmes) + - Alexander Schranz (alexander-schranz) - Sebastiaan Stok (sstok) - Toni Uebernickel (havvg) - Bart van den Burg (burgov) - Jordan Alliot (jalliot) - John Wards (johnwards) - - Alexander Schranz (alexander-schranz) - Baptiste Clavié (talus) - Antoine Hérault (herzult) - Paráda József (paradajozsef) + - Vincent Langlet (deviling) - Arnaud Le Blanc (arnaud-lb) - Przemysław Bogusz (przemyslaw-bogusz) - - Vincent Langlet (deviling) - Maxime STEINHAUSSER - Tomas Norkūnas (norkunas) - Michal Piotrowski (eventhorizon) @@ -126,13 +126,13 @@ The Symfony Connect username in parenthesis allows to get more information - Massimiliano Arione (garak) - Mathias Arlaud (mtarld) - Tim Nagel (merk) + - HypeMC (hypemc) - Chris Wilkinson (thewilkybarkid) - Peter Kokot (maastermedia) - Lars Strojny (lstrojny) - Brice BERNARD (brikou) - Ahmed TAILOULOUTE (ahmedtai) - Gregor Harlan (gharlan) - - HypeMC (hypemc) - marc.weistroff - lenar - Alexander Schwenn (xelaris) @@ -148,6 +148,7 @@ The Symfony Connect username in parenthesis allows to get more information - Théo FIDRY (theofidry) - Florian Voutzinos (florianv) - Teoh Han Hui (teohhanhui) + - Alexandre Daubois (alexandre-daubois) - Colin Frei - Javier Spagnoletti (phansys) - Joshua Thijssen @@ -156,26 +157,26 @@ The Symfony Connect username in parenthesis allows to get more information - excelwebzone - Gordon Franke (gimler) - Saif Eddin Gmati (azjezz) - - Alexandre Daubois (alexandre-daubois) + - Richard van Laak (rvanlaak) - Jesse Rushlow (geeshoe) - Fabien Pennequin (fabienpennequin) + - Mathieu Santostefano (welcomattic) - Olivier Dolbeau (odolbeau) - Smaine Milianni (ismail1432) - - Richard van Laak (rvanlaak) - Eric GELOEN (gelo) + - Gary PEGEOT (gary-p) - Matthieu Napoli (mnapoli) + - Maxime Helias (maxhelias) - Jannik Zschiesche (apfelbox) - - Mathieu Santostefano (welcomattic) - Robert Schönthal (digitalkaoz) - Florian Lonqueu-Brochard (florianlb) - Tigran Azatyan (tigranazatyan) - YaFou - - Gary PEGEOT (gary-p) - Gabriel Caruso (carusogabriel) + - Ruud Kamphuis (ruudk) - Stefano Sala (stefano.sala) - Andréia Bohner (andreia) - Evgeniy (ewgraf) - - Maxime Helias (maxhelias) - Vincent AUBERT (vincent) - Juti Noppornpitak (shiroyuki) - Anthony MARTIN (xurudragon) @@ -184,11 +185,12 @@ The Symfony Connect username in parenthesis allows to get more information - Hidenori Goto (hidenorigoto) - Jan Rosier (rosier) - Alessandro Chitolina (alekitto) - - Ruud Kamphuis (ruudk) + - Ion Bazan (ionbazan) - Albert Casademont (acasademont) - Arnaud Kleinpeter (nanocom) - Guilherme Blanco (guilhermeblanco) - SpacePossum + - Alexander Menshchikov (zmey_kk) - Pablo Godel (pgodel) - Andreas Braun - Jérémie Augustin (jaugustin) @@ -201,7 +203,6 @@ The Symfony Connect username in parenthesis allows to get more information - Jeroen Spee (jeroens) - Fabien Bourigault (fbourigault) - Joe Bennett (kralos) - - Alexander Menshchikov (zmey_kk) - Mikael Pajunen - Andreas Schempp (aschempp) - Romaric Drigon (romaricdrigon) @@ -241,6 +242,7 @@ The Symfony Connect username in parenthesis allows to get more information - Dmitrii Poddubnyi (karser) - Michael Babker (mbabker) - Tien Vo (tienvx) + - Simon Berger - Timothée Barray (tyx) - James Halsall (jaitsu) - Florent Mata (fmata) @@ -270,7 +272,6 @@ The Symfony Connect username in parenthesis allows to get more information - Sebastien Morel (plopix) - Baptiste Leduc (korbeil) - mcfedr (mcfedr) - - Simon Berger - Ruben Gonzalez (rubenrua) - Benjamin Dulau (dbenjamin) - Baptiste Lafontaine (magnetik) @@ -282,7 +283,6 @@ The Symfony Connect username in parenthesis allows to get more information - Guillaume Pédelagrabe - Noel Guilbert (noel) - Anthony GRASSIOT (antograssiot) - - Ion Bazan (ionbazan) - Stadly - Stepan Anchugov (kix) - François Pluchino (francoispluchino) @@ -351,6 +351,7 @@ The Symfony Connect username in parenthesis allows to get more information - Sébastien Lavoie (lavoiesl) - Dariusz - Farhad Safarov (safarov) + - BoShurik - Thomas Lallement (raziel057) - Francois Zaninotto - Claude Khedhiri (ck-developer) @@ -429,6 +430,7 @@ The Symfony Connect username in parenthesis allows to get more information - Wouter Van Hecke - Iker Ibarguren (ikerib) - Bob van de Vijver (bobvandevijver) + - Soner Sayakci - Peter Kruithof (pkruithof) - Michael Holm (hollo) - Sylvain Fabre (sylfabre) @@ -499,7 +501,6 @@ The Symfony Connect username in parenthesis allows to get more information - ivan - Greg Anderson - Tri Pham (phamuyentri) - - BoShurik - Gennady Telegin (gtelegin) - Krystian Marcisz (simivar) - Toni Rudolf (toooni) @@ -524,8 +525,8 @@ The Symfony Connect username in parenthesis allows to get more information - Dmytro Borysovskyi (dmytr0) - Tomasz Kowalczyk (thunderer) - Artur Eshenbrener - - Soner Sayakci - Thomas Perez (scullwm) + - Yoann RENARD (yrenard) - Felix Labrecque - Yaroslav Kiliba - Terje Bråten @@ -619,6 +620,7 @@ The Symfony Connect username in parenthesis allows to get more information - Alexandru Furculita (afurculita) - Valentin Jonovs (valentins-jonovs) - Bastien DURAND (deamon) + - Antonio Jose Cerezo (ajcerezo) - Jeanmonod David (jeanmonod) - Christin Gruber (christingruber) - Andrey Sevastianov @@ -670,7 +672,6 @@ The Symfony Connect username in parenthesis allows to get more information - Andrew M-Y (andr) - Krasimir Bosilkov (kbosilkov) - Marcin Michalski (marcinmichalski) - - Yoann RENARD (yrenard) - Vitaliy Tverdokhlib (vitaliytv) - Ariel Ferrandini (aferrandini) - Niklas Keller @@ -694,6 +695,7 @@ The Symfony Connect username in parenthesis allows to get more information - franek (franek) - Raulnet - Christian Wahler + - Dries Vints - Giso Stallenberg (gisostallenberg) - Gintautas Miselis - Rob Bast @@ -714,6 +716,7 @@ The Symfony Connect username in parenthesis allows to get more information - Patrick Reimers (preimers) - insekticid - Alexander Obuhovich (aik099) + - Jérémy M (th3mouk) - Vitaliy Ryaboy (vitaliy) - boombatower - Fabrice Bernhard (fabriceb) @@ -753,6 +756,7 @@ The Symfony Connect username in parenthesis allows to get more information - ondrowan - Barry vd. Heuvel (barryvdh) - Jon Dufresne + - Fabien S (bafs) - Evan S Kaufman (evanskaufman) - Alex Bacart - mcben @@ -773,6 +777,7 @@ The Symfony Connect username in parenthesis allows to get more information - Andrew Udvare (audvare) - alexpods - Dennis Langen (nijusan) + - Hubert Lenoir (hubert_lenoir) - Adam Szaraniec (mimol) - Dariusz Ruminski - Erik Trapman (eriktrapman) @@ -847,6 +852,7 @@ The Symfony Connect username in parenthesis allows to get more information - Thiago Cordeiro (thiagocordeiro) - Jan Behrens - Dragos Protung (dragosprotung) + - Romain Monteil (ker0x) - Mantas Var (mvar) - Terje Bråten - Yann LUCAS (drixs6o9) @@ -872,7 +878,6 @@ The Symfony Connect username in parenthesis allows to get more information - Jean-Christophe Cuvelier [Artack] - julien57 - Julien Montel (julienmgel) - - Antonio Jose Cerezo (ajcerezo) - Mátyás Somfai (smatyas) - Alexandre Tranchant (alexandre_t) - Anthony Moutte @@ -1114,11 +1119,13 @@ The Symfony Connect username in parenthesis allows to get more information - Aurélien Fontaine - Pascal Helfenstein - Baldur Rensch (brensch) + - Carl Casbolt (carlcasbolt) - Vladyslav Petrovych - Hugo Sales - Alex Xandra Albert Sim - Carson Full - Sergey Yastrebov + - kylekatarnls (kylekatarnls) - Trent Steel (trsteel88) - Yuen-Chi Lian - Tarjei Huse (tarjei) @@ -1175,6 +1182,7 @@ The Symfony Connect username in parenthesis allows to get more information - Christian Soronellas (theunic) - kick-the-bucket - fedor.f + - Bilge - Yosmany Garcia (yosmanyga) - Jeremiasz Major - Wouter de Wild @@ -1186,7 +1194,6 @@ The Symfony Connect username in parenthesis allows to get more information - Krzysiek Łabuś - Juraj Surman - Camille Dejoye - - Fabien S (bafs) - 1ma (jautenim) - Douglas Hammond (wizhippo) - Xavier Lacot (xavier) @@ -1210,7 +1217,6 @@ The Symfony Connect username in parenthesis allows to get more information - Dmitry Pigin (dotty) - Vincent Composieux (eko) - Jayson Xu (superjavason) - - Hubert Lenoir (hubert_lenoir) - fago - popnikos - Tito Costa @@ -1235,14 +1241,15 @@ The Symfony Connect username in parenthesis allows to get more information - Reen Lokum - Martin Parsiegla (spea) - Bernhard Rusch + - bhavin (bhavin4u) - Ivan - Quentin Schuler + - Nico Haase - Pierre Vanliefland (pvanliefland) - Roy Klutman (royklutman) - Sofiane HADDAG (sofhad) - frost-nzcr4 - Taylor Otwell - - Dries Vints - Sami Mussbach - Kien Nguyen - Foxprodev @@ -1279,6 +1286,7 @@ The Symfony Connect username in parenthesis allows to get more information - Cyrille Bourgois (cyrilleb) - Gerard van Helden (drm) - Johnny Peck (johnnypeck) + - Jordi Sala Morales (jsala) - Marcos Rezende (rezehnde) - Roman Anasal - Ivan Menshykov @@ -1324,7 +1332,6 @@ The Symfony Connect username in parenthesis allows to get more information - abdul malik ikhsan (samsonasik) - Henry Snoek (snoek09) - Dmitry (staratel) - - Jérémy M (th3mouk) - Tito Miguel Costa (titomiguelcosta) - Simone Di Maulo (toretto460) - Christian Morgan @@ -1355,6 +1362,7 @@ The Symfony Connect username in parenthesis allows to get more information - Arno Geurts - Adán Lobato (adanlobato) - Ian Jenkins (jenkoian) + - Kai Eichinger (kai_eichinger) - Hugo Alliaume (kocal) - Marcos Gómez Vilches (markitosgv) - Matthew Davis (mdavis1982) @@ -1404,6 +1412,7 @@ The Symfony Connect username in parenthesis allows to get more information - Vincent MOULENE (vints24) - Koen Kuipers - datibbaw + - Nicolas de Marqué (nicola) - Antoine Leblanc - Andre Johnson - Marco Pfeiffer @@ -1412,7 +1421,6 @@ The Symfony Connect username in parenthesis allows to get more information - Daniel Alejandro Castro Arellano (lexcast) - Aleksandar Dimitrov (netbull) - Gary Houbre (thegarious) - - Romain Monteil (ker0x) - sensio - Thomas Jarrand - Antoine Bluchet (soyuka) @@ -1421,6 +1429,7 @@ The Symfony Connect username in parenthesis allows to get more information - Paul Oms - Reece Fowell (reecefowell) - stefan.r + - Htun Htun Htet (ryanhhh91) - Guillaume Gammelin - Valérian Galliat - d-ph @@ -1490,6 +1499,7 @@ The Symfony Connect username in parenthesis allows to get more information - Ken Stanley - ivan - Zachary Tong (polyfractal) + - Oleg Krasavin (okwinza) - Mario Blažek (marioblazek) - Jure (zamzung) - Michael Nelson @@ -1562,6 +1572,7 @@ The Symfony Connect username in parenthesis allows to get more information - Antanas Arvasevicius - Pierre Dudoret - Thomas + - Georgi Georgiev - Maximilian Berghoff (electricmaxxx) - nacho - Piotr Antosik (antek88) @@ -1621,6 +1632,7 @@ The Symfony Connect username in parenthesis allows to get more information - Maximilian Ruta (deltachaos) - Mickaël Isaert (misaert) - Jakub Sacha + - Julius Kiekbusch - Olaf Klischat - orlovv - Claude Dioudonnat @@ -1644,6 +1656,7 @@ The Symfony Connect username in parenthesis allows to get more information - James Hudson - Stephen Clouse - e-ivanov + - Nathanaël Martel (nathanaelmartel) - Einenlum - Jochen Bayer (jocl) - Patrick Carlo-Hickman @@ -1669,6 +1682,7 @@ The Symfony Connect username in parenthesis allows to get more information - Neil Katin - David Otton - Will Donohoe + - gnito-org - peter - Jérémy Jourdin (jjk801) - BRAMILLE Sébastien (oktapodia) @@ -1697,7 +1711,6 @@ The Symfony Connect username in parenthesis allows to get more information - Juan Miguel Besada Vidal (soutlink) - dlorek - Stuart Fyfe - - Carl Casbolt (carlcasbolt) - David de Boer (ddeboer) - Eno Mullaraj (emullaraj) - Nathan PAGE (nathix) @@ -1752,6 +1765,7 @@ The Symfony Connect username in parenthesis allows to get more information - Clement Herreman (clemherreman) - Dan Ionut Dumitriu (danionut90) - Vladislav Rastrusny (fractalizer) + - Vlad Gapanovich (gapik) - Alexander Kurilo (kamazee) - Nyro (nyro) - Marco @@ -1801,6 +1815,7 @@ The Symfony Connect username in parenthesis allows to get more information - Artem Stepin (astepin) - Christian Flach (cmfcmf) - Cédric Girard (enk_) + - Fabian Kropfhamer (fabiank) - Lars Ambrosius Wallenborn (larsborn) - Oriol Mangas Abellan (oriolman) - Sebastian Göttschkes (sgoettschkes) @@ -1912,7 +1927,6 @@ The Symfony Connect username in parenthesis allows to get more information - Martin Pärtel - Daniel Rotter (danrot) - Frédéric Bouchery (fbouchery) - - kylekatarnls (kylekatarnls) - Patrick Daley (padrig) - Foxprodev - Max Summe @@ -1923,6 +1937,7 @@ The Symfony Connect username in parenthesis allows to get more information - Tadcka - Beth Binkovitz - Gonzalo Míguez + - Fabian Haase - Romain Geissler - Adrien Moiruad - Tomaz Ahlin @@ -1994,6 +2009,7 @@ The Symfony Connect username in parenthesis allows to get more information - Ergie Gonzaga - Matthew J Mucklo - AnrDaemon + - Anthony Massard (decap94) - Emre Akinci (emre) - Chris Maiden (matason) - fdgdfg (psampaz) @@ -2119,6 +2135,7 @@ The Symfony Connect username in parenthesis allows to get more information - Sergey Fokin (tyraelqp) - Evrard Boulou - pborreli + - Bernat Llibre - Boris Betzholz - Eric Caron - 2manypeople @@ -2234,10 +2251,12 @@ The Symfony Connect username in parenthesis allows to get more information - Felix Marezki - Normunds - Luiz “Felds” Liscia + - Yuri Karaban - Johan - Thomas Rothe - Martin - nietonfir + - Andriy - alefranz - David Barratt - Andrea Giannantonio @@ -2278,7 +2297,6 @@ The Symfony Connect username in parenthesis allows to get more information - Alessio Baglio (ioalessio) - Johannes Müller (johmue) - Jordi Llonch (jordillonch) - - Jordi Sala Morales (jsala) - Mouad ZIANI (mouadziani) - Nicholas Ruunu (nicholasruunu) - Jeroen van den Nieuwenhuisen (nieuwenhuisen) @@ -2473,8 +2491,10 @@ The Symfony Connect username in parenthesis allows to get more information - Maerlyn - Even André Fiskvik - Agata + - dakur - Александр Ли - Arjan Keeman + - Vlad Dumitrache - Erik van Wingerden - Valouleloup - robmro27 @@ -2602,6 +2622,7 @@ The Symfony Connect username in parenthesis allows to get more information - Paulius Jarmalavičius (pjarmalavicius) - Ramon Henrique Ornelas (ramonornela) - Ricardo de Vries (ricknox) + - Ruslan Zavacky (ruslanzavacky) - Stefano Cappellini (stefano_cappellini) - Thomas Dutrion (theocrite) - Till Klampaeckel (till) @@ -2639,7 +2660,6 @@ The Symfony Connect username in parenthesis allows to get more information - Jordan Hoff - znerol - Christian Eikermann - - Kai Eichinger - Antonio Angelino - Jens Schulze - Matt Fields @@ -2702,10 +2722,12 @@ The Symfony Connect username in parenthesis allows to get more information - Adrian Philipp - James Michael DuPont - Kasperki + - dima-gr - Tammy D - Rodolfo Ruiz - Enrico - Ryan Rud + - Christopher Georg - Ondrej Slinták - vlechemin - Brian Corrigan @@ -2798,6 +2820,7 @@ The Symfony Connect username in parenthesis allows to get more information - Дмитрий Пацура - Signor Pedro - Matthias Larisch + - Maxime P - ilyes kooli - Ilia Lazarev - Michaël VEROUX @@ -2806,7 +2829,6 @@ The Symfony Connect username in parenthesis allows to get more information - arduanov - sualko - Molkobain - - Bilge - Yendric - ADmad - Nicolas Roudaire @@ -2978,7 +3000,6 @@ The Symfony Connect username in parenthesis allows to get more information - Maxime COLIN (maximecolin) - Muharrem Demirci (mdemirci) - Evgeny Z (meze) - - Nicolas de Marqué (nicola) - Pierre Geyer (ptheg) - Thomas BERTRAND (sevrahk) - Matej Žilák (teo_sk) diff --git a/UPGRADE-5.3.md b/UPGRADE-5.3.md index 6a1552e3a8700..166e2ba9b8d33 100644 --- a/UPGRADE-5.3.md +++ b/UPGRADE-5.3.md @@ -106,9 +106,6 @@ Routing Security -------- - * Deprecate using `UsageTrackingTokenStorage` with tracking enabled without a main request. Use the untracked token - storage (service ID: `security.untracked_token_storage`) instead, or disable usage tracking - completely using `UsageTrackingTokenStorage::disableUsageTracking()`. * [BC BREAK] Remove method `checkIfCompletelyResolved()` from `PassportInterface`, checking that passport badges are resolved is up to `AuthenticatorManager` * Deprecate class `User`, use `InMemoryUser` or your own implementation instead. diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php index f9bd78619d6b5..5a0514e323071 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php @@ -87,7 +87,7 @@ public function __construct($message, array $trace, $file) $this->getOriginalFilesStack(); array_splice($this->originalFilesStack, 0, $j, [$this->triggeringFile]); - if (preg_match('/(?|"([^"]++)" that is deprecated|should implement method "(?:static )?([^:]++))/', $message, $m) || preg_match('/^(?:The|Method) "([^":]++)/', $message, $m)) { + if (preg_match('/(?|"([^"]++)" that is deprecated|should implement method "(?:static )?([^:]++))/', $message, $m) || (false === strpos($message, '()" will return') && false === strpos($message, 'native return type declaration') && preg_match('/^(?:The|Method) "([^":]++)/', $message, $m))) { $this->triggeringFile = (new \ReflectionClass($m[1]))->getFileName(); array_unshift($this->originalFilesStack, $this->triggeringFile); } diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php index c92b47f3c0236..d8b546a6f8b27 100644 --- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php +++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php @@ -94,8 +94,8 @@ }; if (\PHP_VERSION_ID >= 80000) { - // PHP 8 requires PHPUnit 9.3+ - $PHPUNIT_VERSION = $getEnvVar('SYMFONY_PHPUNIT_VERSION', '9.4') ?: '9.4'; + // PHP 8 requires PHPUnit 9.3+, PHP 8.1 requires PHPUnit 9.5+ + $PHPUNIT_VERSION = $getEnvVar('SYMFONY_PHPUNIT_VERSION', '9.5') ?: '9.5'; } elseif (\PHP_VERSION_ID >= 70200) { // PHPUnit 8 requires PHP 7.2+ $PHPUNIT_VERSION = $getEnvVar('SYMFONY_PHPUNIT_VERSION', '8.5') ?: '8.5'; @@ -122,7 +122,7 @@ } $oldPwd = getcwd(); -$PHPUNIT_DIR = $getEnvVar('SYMFONY_PHPUNIT_DIR', $root.'/vendor/bin/.phpunit'); +$PHPUNIT_DIR = rtrim($getEnvVar('SYMFONY_PHPUNIT_DIR', $root.'/vendor/bin/.phpunit'), '/'.\DIRECTORY_SEPARATOR); $PHP = defined('PHP_BINARY') ? \PHP_BINARY : 'php'; $PHP = escapeshellarg($PHP); if ('phpdbg' === \PHP_SAPI) { @@ -239,6 +239,10 @@ $passthruOrFail("$COMPOSER config --unset platform.php"); } if (file_exists($path = $root.'/vendor/symfony/phpunit-bridge')) { + $p = str_repeat('../', substr_count("$PHPUNIT_DIR/$PHPUNIT_VERSION_DIR", '/', strlen($root))).'vendor/symfony/phpunit-bridge'; + if (realpath($p) === realpath($path)) { + $path = $p; + } $passthruOrFail("$COMPOSER require --no-update symfony/phpunit-bridge \"*@dev\""); $passthruOrFail("$COMPOSER config repositories.phpunit-bridge path ".escapeshellarg(str_replace('/', \DIRECTORY_SEPARATOR, $path))); if ('\\' === \DIRECTORY_SEPARATOR) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php index f6a3063aadf3d..d2db4d4052d59 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php @@ -298,7 +298,7 @@ ->set('console.command.secrets_list', SecretsListCommand::class) ->args([ service('secrets.vault'), - service('secrets.local_vault'), + service('secrets.local_vault')->ignoreOnInvalid(), ]) ->tag('console.command') @@ -312,7 +312,7 @@ ->set('console.command.secrets_encrypt_from_local', SecretsEncryptFromLocalCommand::class) ->args([ service('secrets.vault'), - service('secrets.local_vault'), + service('secrets.local_vault')->ignoreOnInvalid(), ]) ->tag('console.command') ; diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd index bc46570744d16..c86e15751362f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd @@ -344,15 +344,6 @@ - - - - - - - - - diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php index e400b95506b73..d9f4b1192f456 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php @@ -126,7 +126,7 @@ public function testRateLimiterLockFactory() }); $this->expectException(OutOfBoundsException::class); - $this->expectExceptionMessage('The argument "2" doesn\'t exist.'); + $this->expectExceptionMessageMatches('/^The argument "2" doesn\'t exist.*\.$/'); $container->getDefinition('limiter.without_lock')->getArgument(2); } diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php index a18de86e7b02d..12724e0f1cc65 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php @@ -27,19 +27,19 @@ class ExtensionPass implements CompilerPassInterface { public function process(ContainerBuilder $container) { - if (!$container::willBeAvailable('symfony/asset', Packages::class, ['symfony/twig-bundle'])) { + if (!class_exists(Packages::class)) { $container->removeDefinition('twig.extension.assets'); } - if (!$container::willBeAvailable('symfony/expression-language', Expression::class, ['symfony/twig-bundle'])) { + if (!class_exists(Expression::class)) { $container->removeDefinition('twig.extension.expression'); } - if (!$container::willBeAvailable('symfony/routing', UrlGeneratorInterface::class, ['symfony/twig-bundle'])) { + if (!interface_exists(UrlGeneratorInterface::class)) { $container->removeDefinition('twig.extension.routing'); } - if (!$container::willBeAvailable('symfony/yaml', Yaml::class, ['symfony/twig-bundle'])) { + if (!class_exists(Yaml::class)) { $container->removeDefinition('twig.extension.yaml'); } @@ -115,7 +115,7 @@ public function process(ContainerBuilder $container) $container->getDefinition('twig.extension.expression')->addTag('twig.extension'); } - if (!$container::willBeAvailable('symfony/workflow', Workflow::class, ['symfony/twig-bundle']) || !$container->has('workflow.registry')) { + if (!class_exists(Workflow::class) || !$container->has('workflow.registry')) { $container->removeDefinition('workflow.twig_extension'); } else { $container->getDefinition('workflow.twig_extension')->addTag('twig.extension'); diff --git a/src/Symfony/Component/Cache/Adapter/PdoAdapter.php b/src/Symfony/Component/Cache/Adapter/PdoAdapter.php index b54841f0c0fbc..7a47e589e0063 100644 --- a/src/Symfony/Component/Cache/Adapter/PdoAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/PdoAdapter.php @@ -123,7 +123,7 @@ public function createTable() $this->addTableToSchema($schema); foreach ($schema->toSql($conn->getDatabasePlatform()) as $sql) { - if (method_exists($conn, 'executeStatement')) { + if ($conn instanceof Connection && method_exists($conn, 'executeStatement')) { $conn->executeStatement($sql); } else { $conn->exec($sql); @@ -158,7 +158,7 @@ public function createTable() throw new \DomainException(sprintf('Creating the cache table is currently not implemented for PDO driver "%s".', $this->driver)); } - if (method_exists($conn, 'executeStatement')) { + if ($conn instanceof Connection && method_exists($conn, 'executeStatement')) { $conn->executeStatement($sql); } else { $conn->exec($sql); @@ -307,7 +307,7 @@ protected function doClear(string $namespace) } try { - if (method_exists($conn, 'executeStatement')) { + if ($conn instanceof Connection && method_exists($conn, 'executeStatement')) { $conn->executeStatement($sql); } else { $conn->exec($sql); diff --git a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php index cd0eaa774bc49..a9048995c2abb 100644 --- a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php @@ -157,9 +157,10 @@ public function invalidateTags(array $tags) */ public function hasItem($key) { - if ($this->deferred) { + if (\is_string($key) && isset($this->deferred[$key])) { $this->commit(); } + if (!$this->pool->hasItem($key)) { return false; } @@ -200,18 +201,21 @@ public function getItem($key) */ public function getItems(array $keys = []) { - if ($this->deferred) { - $this->commit(); - } $tagKeys = []; + $commit = false; foreach ($keys as $key) { if ('' !== $key && \is_string($key)) { + $commit = $commit || isset($this->deferred[$key]); $key = static::TAGS_PREFIX.$key; $tagKeys[$key] = $key; } } + if ($commit) { + $this->commit(); + } + try { $items = $this->pool->getItems($tagKeys + $keys); } catch (InvalidArgumentException $e) { diff --git a/src/Symfony/Component/Cache/Traits/AbstractAdapterTrait.php b/src/Symfony/Component/Cache/Traits/AbstractAdapterTrait.php index ca7a030fdff2d..b34aa466ceb77 100644 --- a/src/Symfony/Component/Cache/Traits/AbstractAdapterTrait.php +++ b/src/Symfony/Component/Cache/Traits/AbstractAdapterTrait.php @@ -208,10 +208,11 @@ public function deleteItems(array $keys) */ public function getItem($key) { - if ($this->deferred) { + $id = $this->getId($key); + + if (isset($this->deferred[$key])) { $this->commit(); } - $id = $this->getId($key); $isHit = false; $value = null; @@ -234,14 +235,18 @@ public function getItem($key) */ public function getItems(array $keys = []) { - if ($this->deferred) { - $this->commit(); - } $ids = []; + $commit = false; foreach ($keys as $key) { $ids[] = $this->getId($key); + $commit = $commit || isset($this->deferred[$key]); + } + + if ($commit) { + $this->commit(); } + try { $items = $this->doFetch($ids); } catch (\Exception $e) { diff --git a/src/Symfony/Component/Config/Resource/GlobResource.php b/src/Symfony/Component/Config/Resource/GlobResource.php index 2ac06986d5b31..31937bc9037e6 100644 --- a/src/Symfony/Component/Config/Resource/GlobResource.php +++ b/src/Symfony/Component/Config/Resource/GlobResource.php @@ -119,7 +119,7 @@ public function getIterator(): \Traversable } if (null !== $paths) { - sort($paths); + natsort($paths); foreach ($paths as $path) { if ($this->excludedPrefixes) { $normalizedPath = str_replace('\\', '/', $path); @@ -152,7 +152,7 @@ function (\SplFileInfo $file, $path) { ), \RecursiveIteratorIterator::LEAVES_ONLY )); - uasort($files, 'strnatcmp'); + uksort($files, 'strnatcmp'); foreach ($files as $path => $info) { if ($info->isFile()) { diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php index fd90959281713..28931358dd68b 100644 --- a/src/Symfony/Component/Console/Helper/QuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php @@ -205,7 +205,7 @@ protected function formatChoiceQuestionChoices(ChoiceQuestion $question, string { $messages = []; - $maxWidth = max(array_map('self::width', array_keys($choices = $question->getChoices()))); + $maxWidth = max(array_map([__CLASS__, 'width'], array_keys($choices = $question->getChoices()))); foreach ($choices as $key => $value) { $padding = str_repeat(' ', $maxWidth - self::width($key)); diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index ddfc5fd382143..b4c1615b396c7 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -40,6 +40,7 @@ class AutowirePass extends AbstractRecursivePass private $decoratedClass; private $decoratedId; private $methodCalls; + private $defaultArgument; private $getPreviousValue; private $decoratedMethodIndex; private $decoratedMethodArgumentIndex; @@ -48,6 +49,10 @@ class AutowirePass extends AbstractRecursivePass public function __construct(bool $throwOnAutowireException = true) { $this->throwOnAutowiringException = $throwOnAutowireException; + $this->defaultArgument = new class() { + public $value; + public $names; + }; } /** @@ -62,6 +67,7 @@ public function process(ContainerBuilder $container) $this->decoratedClass = null; $this->decoratedId = null; $this->methodCalls = null; + $this->defaultArgument->names = null; $this->getPreviousValue = null; $this->decoratedMethodIndex = null; $this->decoratedMethodArgumentIndex = null; @@ -153,14 +159,13 @@ private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot, $this->decoratedClass = null; $this->getPreviousValue = null; - if ($isRoot && ($definition = $this->container->getDefinition($this->currentId)) && ($decoratedDefinition = $definition->getDecoratedService()) && null !== ($innerId = $decoratedDefinition[0]) && $this->container->has($innerId)) { - // If the class references to itself and is decorated, provide the inner service id and class to not get a circular reference - $this->decoratedClass = $this->container->findDefinition($innerId)->getClass(); - $this->decoratedId = $decoratedDefinition[1] ?? $this->currentId.'.inner'; + if ($isRoot && ($definition = $this->container->getDefinition($this->currentId)) && null !== ($this->decoratedId = $definition->innerServiceId) && $this->container->has($this->decoratedId)) { + $this->decoratedClass = $this->container->findDefinition($this->decoratedId)->getClass(); } + $patchedIndexes = []; + foreach ($this->methodCalls as $i => $call) { - $this->decoratedMethodIndex = $i; [$method, $arguments] = $call; if ($method instanceof \ReflectionFunctionAbstract) { @@ -177,13 +182,39 @@ private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot, } } - $arguments = $this->autowireMethod($reflectionMethod, $arguments, $checkAttributes); + $arguments = $this->autowireMethod($reflectionMethod, $arguments, $checkAttributes, $i); if ($arguments !== $call[1]) { $this->methodCalls[$i][1] = $arguments; + $patchedIndexes[] = $i; } } + // use named arguments to skip complex default values + foreach ($patchedIndexes as $i) { + $namedArguments = null; + $arguments = $this->methodCalls[$i][1]; + + foreach ($arguments as $j => $value) { + if ($namedArguments && !$value instanceof $this->defaultArgument) { + unset($arguments[$j]); + $arguments[$namedArguments[$j]] = $value; + } + if ($namedArguments || !$value instanceof $this->defaultArgument) { + continue; + } + + if (\PHP_VERSION_ID >= 80100 && (\is_array($value->value) ? $value->value : \is_object($value->value))) { + unset($arguments[$j]); + $namedArguments = $value->names; + } else { + $arguments[$j] = $value->value; + } + } + + $this->methodCalls[$i][1] = $arguments; + } + return $this->methodCalls; } @@ -194,7 +225,7 @@ private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot, * * @throws AutowiringFailedException */ - private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, array $arguments, bool $checkAttributes): array + private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, array $arguments, bool $checkAttributes, int $methodIndex): array { $class = $reflectionMethod instanceof \ReflectionMethod ? $reflectionMethod->class : $this->currentId; $method = $reflectionMethod->name; @@ -202,8 +233,11 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a if ($reflectionMethod->isVariadic()) { array_pop($parameters); } + $this->defaultArgument->names = new \ArrayObject(); foreach ($parameters as $index => $parameter) { + $this->defaultArgument->names[$index] = $parameter->name; + if (\array_key_exists($index, $arguments) && '' !== $arguments[$index]) { continue; } @@ -241,7 +275,8 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a // be false when isOptional() returns true. If the // argument *is* optional, allow it to be missing if ($parameter->isOptional()) { - continue; + --$index; + break; } $type = ProxyHelper::getTypeHint($reflectionMethod, $parameter, false); $type = $type ? sprintf('is type-hinted "%s"', ltrim($type, '\\')) : 'has no type-hint'; @@ -250,7 +285,8 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a } // specifically pass the default value - $arguments[$index] = $parameter->getDefaultValue(); + $arguments[$index] = clone $this->defaultArgument; + $arguments[$index]->value = $parameter->getDefaultValue(); continue; } @@ -260,7 +296,8 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a $failureMessage = $this->createTypeNotFoundMessageCallback($ref, sprintf('argument "$%s" of method "%s()"', $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method)); if ($parameter->isDefaultValueAvailable()) { - $value = $parameter->getDefaultValue(); + $value = clone $this->defaultArgument; + $value->value = $parameter->getDefaultValue(); } elseif (!$parameter->allowsNull()) { throw new AutowiringFailedException($this->currentId, $failureMessage); } @@ -281,6 +318,7 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a } else { $arguments[$index] = new TypedReference($this->decoratedId, $this->decoratedClass); $this->getPreviousValue = $getValue; + $this->decoratedMethodIndex = $methodIndex; $this->decoratedMethodArgumentIndex = $index; continue; @@ -292,8 +330,7 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a if ($parameters && !isset($arguments[++$index])) { while (0 <= --$index) { - $parameter = $parameters[$index]; - if (!$parameter->isDefaultValueAvailable() || $parameter->getDefaultValue() !== $arguments[$index]) { + if (!$arguments[$index] instanceof $this->defaultArgument) { break; } unset($arguments[$index]); diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckArgumentsValidityPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckArgumentsValidityPass.php index 348498db269cf..93808b201d220 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/CheckArgumentsValidityPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckArgumentsValidityPass.php @@ -39,7 +39,13 @@ protected function processValue($value, bool $isRoot = false) } $i = 0; + $hasNamedArgs = false; foreach ($value->getArguments() as $k => $v) { + if (\PHP_VERSION_ID >= 80000 && preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $k)) { + $hasNamedArgs = true; + continue; + } + if ($k !== $i++) { if (!\is_int($k)) { $msg = sprintf('Invalid constructor argument for service "%s": integer expected but found string "%s". Check your service definition.', $this->currentId, $k); @@ -57,11 +63,27 @@ protected function processValue($value, bool $isRoot = false) throw new RuntimeException($msg); } } + + if ($hasNamedArgs) { + $msg = sprintf('Invalid constructor argument for service "%s": cannot use positional argument after named argument. Check your service definition.', $this->currentId); + $value->addError($msg); + if ($this->throwExceptions) { + throw new RuntimeException($msg); + } + + break; + } } foreach ($value->getMethodCalls() as $methodCall) { $i = 0; + $hasNamedArgs = false; foreach ($methodCall[1] as $k => $v) { + if (\PHP_VERSION_ID >= 80000 && preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $k)) { + $hasNamedArgs = true; + continue; + } + if ($k !== $i++) { if (!\is_int($k)) { $msg = sprintf('Invalid argument for method call "%s" of service "%s": integer expected but found string "%s". Check your service definition.', $methodCall[0], $this->currentId, $k); @@ -79,6 +101,16 @@ protected function processValue($value, bool $isRoot = false) throw new RuntimeException($msg); } } + + if ($hasNamedArgs) { + $msg = sprintf('Invalid argument for method call "%s" of service "%s": cannot use positional argument after named argument. Check your service definition.', $methodCall[0], $this->currentId); + $value->addError($msg); + if ($this->throwExceptions) { + throw new RuntimeException($msg); + } + + break; + } } } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php index fe7d29e26e210..ff72ce41ced26 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php @@ -110,7 +110,7 @@ public function process(ContainerBuilder $container) protected function processValue($value, bool $isRoot = false) { if ($value instanceof ArgumentInterface) { - // Reference found in ArgumentInterface::getValues() are not inlineable + // References found in ArgumentInterface::getValues() are not inlineable return $value; } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php index 47f51c2478940..ed8e696bb2853 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php @@ -62,10 +62,11 @@ public function __construct() new AutowireRequiredMethodsPass(), new AutowireRequiredPropertiesPass(), new ResolveBindingsPass(), + new ServiceLocatorTagPass(), + new DecoratorServicePass(), new CheckDefinitionValidityPass(), new AutowirePass(false), new ServiceLocatorTagPass(), - new DecoratorServicePass(), new ResolveTaggedIteratorArgumentPass(), new ResolveServiceSubscribersPass(), new ResolveReferencesToAliasesPass(), diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index da745850d5713..cd7ae70b8afdf 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -739,8 +739,8 @@ private function addServiceMethodCalls(Definition $definition, string $variableN $calls = ''; foreach ($definition->getMethodCalls() as $k => $call) { $arguments = []; - foreach ($call[1] as $value) { - $arguments[] = $this->dumpValue($value); + foreach ($call[1] as $i => $value) { + $arguments[] = (\is_string($i) ? $i.': ' : '').$this->dumpValue($value); } $witherAssignation = ''; @@ -1132,8 +1132,8 @@ private function addNewInstance(Definition $definition, string $return = '', str } $arguments = []; - foreach ($definition->getArguments() as $value) { - $arguments[] = $this->dumpValue($value); + foreach ($definition->getArguments() as $i => $value) { + $arguments[] = (\is_string($i) ? $i.': ' : '').$this->dumpValue($value); } if (null !== $definition->getFactory()) { diff --git a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php index 9a28350796d76..8a454e9f00722 100644 --- a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php +++ b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php @@ -258,10 +258,8 @@ public function getEnv(string $prefix, string $name, \Closure $getEnv) 'fragment' => null, ]; - if (null !== $parsedEnv['path']) { - // remove the '/' separator - $parsedEnv['path'] = '/' === $parsedEnv['path'] ? null : substr($parsedEnv['path'], 1); - } + // remove the '/' separator + $parsedEnv['path'] = '/' === ($parsedEnv['path'] ?? '/') ? '' : substr($parsedEnv['path'], 1); return $parsedEnv; } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index 33e9adecfb4ea..9867e73293653 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -985,8 +985,8 @@ public function testAutowireDecorator() ->setAutowired(true) ; - (new AutowirePass())->process($container); (new DecoratorServicePass())->process($container); + (new AutowirePass())->process($container); $definition = $container->getDefinition(Decorator::class); $this->assertSame(Decorator::class.'.inner', (string) $definition->getArgument(1)); @@ -1008,8 +1008,8 @@ public function testAutowireDecoratorChain() ->setAutowired(true) ; - (new AutowirePass())->process($container); (new DecoratorServicePass())->process($container); + (new AutowirePass())->process($container); $definition = $container->getDefinition(DecoratedDecorator::class); $this->assertSame(DecoratedDecorator::class.'.inner', (string) $definition->getArgument(0)); @@ -1026,8 +1026,8 @@ public function testAutowireDecoratorRenamedId() ->setAutowired(true) ; - (new AutowirePass())->process($container); (new DecoratorServicePass())->process($container); + (new AutowirePass())->process($container); $definition = $container->getDefinition(Decorator::class); $this->assertSame('renamed', (string) $definition->getArgument(1)); @@ -1044,11 +1044,12 @@ public function testDoNotAutowireDecoratorWhenSeveralArgumentOfTheType() ->setAutowired(true) ; + (new DecoratorServicePass())->process($container); try { (new AutowirePass())->process($container); $this->fail('AutowirePass should have thrown an exception'); } catch (AutowiringFailedException $e) { - $this->assertSame('Cannot autowire service "Symfony\Component\DependencyInjection\Tests\Compiler\NonAutowirableDecorator": argument "$decorated1" of method "__construct()" references interface "Symfony\Component\DependencyInjection\Tests\Compiler\DecoratorInterface" but no such service exists. You should maybe alias this interface to one of these existing services: "Symfony\Component\DependencyInjection\Tests\Compiler\Decorated", "Symfony\Component\DependencyInjection\Tests\Compiler\NonAutowirableDecorator".', (string) $e->getMessage()); + $this->assertSame('Cannot autowire service "Symfony\Component\DependencyInjection\Tests\Compiler\NonAutowirableDecorator": argument "$decorated1" of method "__construct()" references interface "Symfony\Component\DependencyInjection\Tests\Compiler\DecoratorInterface" but no such service exists. You should maybe alias this interface to one of these existing services: "Symfony\Component\DependencyInjection\Tests\Compiler\NonAutowirableDecorator", "Symfony\Component\DependencyInjection\Tests\Compiler\NonAutowirableDecorator.inner".', (string) $e->getMessage()); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckArgumentsValidityPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckArgumentsValidityPassTest.php index 1253abb37ff80..322dcd2583fbe 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckArgumentsValidityPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckArgumentsValidityPassTest.php @@ -46,22 +46,22 @@ public function testProcess() */ public function testException(array $arguments, array $methodCalls) { - $this->expectException(RuntimeException::class); $container = new ContainerBuilder(); $definition = $container->register('foo'); $definition->setArguments($arguments); $definition->setMethodCalls($methodCalls); $pass = new CheckArgumentsValidityPass(); + $this->expectException(RuntimeException::class); $pass->process($container); } public function definitionProvider() { return [ - [[null, 'a' => 'a'], []], + [['a' => 'a', null], []], [[1 => 1], []], - [[], [['baz', [null, 'a' => 'a']]]], + [[], [['baz', ['a' => 'a', null]]]], [[], [['baz', [1 => 1]]]], ]; } @@ -70,7 +70,7 @@ public function testNoException() { $container = new ContainerBuilder(); $definition = $container->register('foo'); - $definition->setArguments([null, 'a' => 'a']); + $definition->setArguments(['a' => 'a', null]); $pass = new CheckArgumentsValidityPass(false); $pass->process($container); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php index 848bb7445e10a..2cc52e91d88e8 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php @@ -175,6 +175,33 @@ public function testCanDecorateServiceLocator() $this->assertSame($container->get('foo'), $container->get(DecoratedServiceLocator::class)->get('foo')); } + public function testAliasDecoratedService() + { + $container = new ContainerBuilder(); + + $container->register('service', ServiceLocator::class) + ->setPublic(true) + ->setArguments([[]]) + ; + $container->register('decorator', DecoratedServiceLocator::class) + ->setDecoratedService('service') + ->setAutowired(true) + ->setPublic(true) + ; + $container->setAlias(ServiceLocator::class, 'decorator.inner') + ->setPublic(true) + ; + $container->register('user_service', DecoratedServiceLocator::class) + ->setAutowired(true) + ; + + $container->compile(); + + $this->assertInstanceOf(DecoratedServiceLocator::class, $container->get('service')); + $this->assertInstanceOf(ServiceLocator::class, $container->get(ServiceLocator::class)); + $this->assertSame($container->get('service'), $container->get('decorator')); + } + /** * @dataProvider getYamlCompileTests */ diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index f49484794d3f4..f3b0528d02ceb 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -45,6 +45,7 @@ use Symfony\Component\DependencyInjection\Tests\Fixtures\FooClassWithEnumAttribute; use Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum; use Symfony\Component\DependencyInjection\Tests\Fixtures\FooWithAbstractArgument; +use Symfony\Component\DependencyInjection\Tests\Fixtures\NewInInitializer; use Symfony\Component\DependencyInjection\Tests\Fixtures\ScalarFactory; use Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator; use Symfony\Component\DependencyInjection\Tests\Fixtures\TestDefinition1; @@ -1207,6 +1208,24 @@ public function testDumpHandlesObjectClassNames() $this->assertInstanceOf(\stdClass::class, $container->get('bar')); } + /** + * @requires PHP 8.1 + */ + public function testNewInInitializer() + { + $container = new ContainerBuilder(); + $container + ->register('foo', NewInInitializer::class) + ->setPublic(true) + ->setAutowired(true) + ->setArguments(['$bar' => 234]); + + $container->compile(); + + $dumper = new PhpDumper($container); + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_new_in_initializer.php', $dumper->dump()); + } + /** * @requires PHP 8.1 */ diff --git a/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php b/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php index 109fb27e7190f..0b84145beda02 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php @@ -640,8 +640,8 @@ public function testGetEnvUrlPath(?string $expected, string $url) public function provideGetEnvUrlPath() { return [ - [null, 'https://symfony.com'], - [null, 'https://symfony.com/'], + ['', 'https://symfony.com'], + ['', 'https://symfony.com/'], ['/', 'https://symfony.com//'], ['blog', 'https://symfony.com/blog'], ['blog/', 'https://symfony.com/blog/'], diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/NewInInitializer.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/NewInInitializer.php new file mode 100644 index 0000000000000..4309ab330bd9c --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/NewInInitializer.php @@ -0,0 +1,10 @@ +services = $this->privates = []; + $this->methodMap = [ + 'foo' => 'getFooService', + ]; + + $this->aliases = []; + } + + public function compile(): void + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled(): bool + { + return true; + } + + public function getRemovedIds(): array + { + return [ + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ]; + } + + /** + * Gets the public 'foo' shared autowired service. + * + * @return \Symfony\Component\DependencyInjection\Tests\Fixtures\NewInInitializer + */ + protected function getFooService() + { + return $this->services['foo'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\NewInInitializer(bar: 234); + } +} diff --git a/src/Symfony/Component/Dotenv/Dotenv.php b/src/Symfony/Component/Dotenv/Dotenv.php index ade7fa5978a0b..4d2cf32ed3dfb 100644 --- a/src/Symfony/Component/Dotenv/Dotenv.php +++ b/src/Symfony/Component/Dotenv/Dotenv.php @@ -191,8 +191,12 @@ public function populate(array $values, bool $overrideExistingVars = false): voi foreach ($values as $name => $value) { $notHttpName = 0 !== strpos($name, 'HTTP_'); + if (isset($_SERVER[$name]) && $notHttpName && !isset($_ENV[$name])) { + $_ENV[$name] = $_SERVER[$name]; + } + // don't check existence with getenv() because of thread safety issues - if (!isset($loadedVars[$name]) && (!$overrideExistingVars && (isset($_ENV[$name]) || (isset($_SERVER[$name]) && $notHttpName)))) { + if (!isset($loadedVars[$name]) && !$overrideExistingVars && isset($_ENV[$name])) { continue; } diff --git a/src/Symfony/Component/Dotenv/Tests/DotenvTest.php b/src/Symfony/Component/Dotenv/Tests/DotenvTest.php index 44244b11d334c..682e81e88b884 100644 --- a/src/Symfony/Component/Dotenv/Tests/DotenvTest.php +++ b/src/Symfony/Component/Dotenv/Tests/DotenvTest.php @@ -226,63 +226,71 @@ public function testLoad() public function testLoadEnv() { - unset($_ENV['FOO']); - unset($_ENV['BAR']); - unset($_SERVER['FOO']); - unset($_SERVER['BAR']); - putenv('FOO'); - putenv('BAR'); + $resetContext = static function (): void { + unset($_ENV['SYMFONY_DOTENV_VARS']); + unset($_ENV['FOO']); + unset($_ENV['TEST_APP_ENV']); + unset($_SERVER['SYMFONY_DOTENV_VARS']); + unset($_SERVER['FOO']); + unset($_SERVER['TEST_APP_ENV']); + putenv('SYMFONY_DOTENV_VARS'); + putenv('FOO'); + putenv('TEST_APP_ENV'); + }; @mkdir($tmpdir = sys_get_temp_dir().'/dotenv'); $path = tempnam($tmpdir, 'sf-'); // .env - file_put_contents($path, 'FOO=BAR'); + + $resetContext(); (new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV'); $this->assertSame('BAR', getenv('FOO')); $this->assertSame('dev', getenv('TEST_APP_ENV')); // .env.local + file_put_contents("$path.local", 'FOO=localBAR'); + $resetContext(); $_SERVER['TEST_APP_ENV'] = 'local'; - file_put_contents("$path.local", 'FOO=localBAR'); (new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV'); $this->assertSame('localBAR', getenv('FOO')); // special case for test - + $resetContext(); $_SERVER['TEST_APP_ENV'] = 'test'; (new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV'); $this->assertSame('BAR', getenv('FOO')); // .env.dev - - unset($_SERVER['TEST_APP_ENV']); file_put_contents("$path.dev", 'FOO=devBAR'); + + $resetContext(); (new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV'); $this->assertSame('devBAR', getenv('FOO')); // .env.dev.local - file_put_contents("$path.dev.local", 'FOO=devlocalBAR'); + + $resetContext(); (new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV'); $this->assertSame('devlocalBAR', getenv('FOO')); + unlink("$path.local"); + unlink("$path.dev"); + unlink("$path.dev.local"); // .env.dist + file_put_contents("$path.dist", 'FOO=distBAR'); + $resetContext(); unlink($path); - file_put_contents("$path.dist", 'BAR=distBAR'); (new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV'); - $this->assertSame('distBAR', getenv('BAR')); - - putenv('FOO'); - putenv('BAR'); + $this->assertSame('distBAR', getenv('FOO')); unlink("$path.dist"); - unlink("$path.local"); - unlink("$path.dev"); - unlink("$path.dev.local"); + + $resetContext(); rmdir($tmpdir); } @@ -475,26 +483,42 @@ public function testDoNotUsePutenv() $this->assertFalse(getenv('TEST_USE_PUTENV')); } + public function testSERVERVarsDuplicationInENV() + { + unset($_ENV['SYMFONY_DOTENV_VARS'], $_SERVER['SYMFONY_DOTENV_VARS'], $_ENV['FOO']); + $_SERVER['FOO'] = 'CCC'; + + (new Dotenv())->populate(['FOO' => 'BAR']); + + $this->assertSame('CCC', $_ENV['FOO']); + } + public function testBootEnv() { + $resetContext = static function (): void { + unset($_SERVER['SYMFONY_DOTENV_VARS'], $_ENV['SYMFONY_DOTENV_VARS']); + unset($_SERVER['TEST_APP_ENV'], $_ENV['TEST_APP_ENV']); + unset($_SERVER['TEST_APP_DEBUG'], $_ENV['TEST_APP_DEBUG']); + unset($_SERVER['FOO'], $_ENV['FOO']); + }; + @mkdir($tmpdir = sys_get_temp_dir().'/dotenv'); $path = tempnam($tmpdir, 'sf-'); file_put_contents($path, 'FOO=BAR'); + $resetContext(); (new Dotenv('TEST_APP_ENV', 'TEST_APP_DEBUG'))->bootEnv($path); - $this->assertSame('BAR', $_SERVER['FOO']); - - unset($_SERVER['FOO'], $_ENV['FOO']); unlink($path); file_put_contents($path.'.local.php', ' "dev", "FOO" => "BAR"];'); + $resetContext(); (new Dotenv('TEST_APP_ENV', 'TEST_APP_DEBUG'))->bootEnv($path); $this->assertSame('BAR', $_SERVER['FOO']); $this->assertSame('1', $_SERVER['TEST_APP_DEBUG']); - - unset($_SERVER['FOO'], $_ENV['FOO']); unlink($path.'.local.php'); + + $resetContext(); rmdir($tmpdir); } } diff --git a/src/Symfony/Component/Form/Extension/Core/CoreExtension.php b/src/Symfony/Component/Form/Extension/Core/CoreExtension.php index 9c19603df003a..c6768b86b497a 100644 --- a/src/Symfony/Component/Form/Extension/Core/CoreExtension.php +++ b/src/Symfony/Component/Form/Extension/Core/CoreExtension.php @@ -45,7 +45,7 @@ protected function loadTypes() new Type\FormType($this->propertyAccessor), new Type\BirthdayType(), new Type\CheckboxType(), - new Type\ChoiceType($this->choiceListFactory), + new Type\ChoiceType($this->choiceListFactory, $this->translator), new Type\CollectionType(), new Type\CountryType(), new Type\DateIntervalType(), diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index c2899bbd78ad9..6b8757c075b15 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -63,6 +63,11 @@ public function __construct(ChoiceListFactoryInterface $choiceListFactory = null ) ); + if (null !== $translator && !$translator instanceof TranslatorInterface) { + throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be han instance of "%s", "%s" given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); + } + $this->translator = $translator; + // BC, to be removed in 6.0 if ($this->choiceListFactory instanceof CachingFactoryDecorator) { return; @@ -73,11 +78,6 @@ public function __construct(ChoiceListFactoryInterface $choiceListFactory = null if ($ref->getNumberOfParameters() < 3) { trigger_deprecation('symfony/form', '5.1', 'Not defining a third parameter "callable|null $filter" in "%s::%s()" is deprecated.', $ref->class, $ref->name); } - - if (null !== $translator && !$translator instanceof TranslatorInterface) { - throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be han instance of "%s", "%s" given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); - } - $this->translator = $translator; } /** diff --git a/src/Symfony/Component/Form/Resources/translations/validators.bs.xlf b/src/Symfony/Component/Form/Resources/translations/validators.bs.xlf index 259b05f842995..319f91544d50c 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.bs.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.bs.xlf @@ -16,7 +16,7 @@ This value is not a valid HTML5 color. - Ova vrijednost nije ispravna HTML5 boja. + Ova vrijednost nije važeća HTML5 boja. Please enter a valid birthdate. diff --git a/src/Symfony/Component/Form/Resources/translations/validators.fa.xlf b/src/Symfony/Component/Form/Resources/translations/validators.fa.xlf index 1bbe090f34472..4ed719917549d 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.fa.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.fa.xlf @@ -24,7 +24,7 @@ The selected choice is invalid. - گزینه‌ی انتخاب‌شده نامعتبر است + گزینه‌ی انتخاب‌شده نامعتبر است. The collection is invalid. @@ -40,7 +40,7 @@ Please select a valid currency. - لطفاً یک واحد پولی معتبر انتخاب کنید. + لطفاً یک واحد پول معتبر انتخاب کنید. Please choose a valid date interval. @@ -72,7 +72,7 @@ Please select a valid locale. - لطفاً یک جغرافیای (locale) معتبر انتخاب کنید. + لطفاً یک منطقه‌جغرافیایی (locale) معتبر انتخاب کنید. Please enter a valid money amount. @@ -116,11 +116,11 @@ The checkbox has an invalid value. - کادر انتخاب (checkbox) دارای مقداری نامعتبر است. + کادر انتخاب (checkbox) دارای مقداری نامعتبر است. Please enter a valid email address. - لطفاً یک آدرس رایانامه‌ی معتبر وارد کنید. + لطفاً یک آدرس رایانامه (ایمیل) معتبر وارد کنید. Please select a valid option. diff --git a/src/Symfony/Component/Form/Resources/translations/validators.fr.xlf b/src/Symfony/Component/Form/Resources/translations/validators.fr.xlf index f40dea752d3dd..d65826467229f 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.fr.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.fr.xlf @@ -4,7 +4,7 @@ This form should not contain extra fields. - Ce formulaire ne doit pas contenir des champs supplémentaires. + Ce formulaire ne doit pas contenir de champs supplémentaires. The uploaded file was too large. Please try to upload a smaller file. diff --git a/src/Symfony/Component/Form/Resources/translations/validators.hr.xlf b/src/Symfony/Component/Form/Resources/translations/validators.hr.xlf index a04ab1283f840..9f17b5ea1eb37 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.hr.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.hr.xlf @@ -16,7 +16,7 @@ This value is not a valid HTML5 color. - Ova vrijednost nije ispravna HTML5 boja. + Ova vrijednost nije važeća HTML5 boja. Please enter a valid birthdate. diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTranslationTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTranslationTest.php new file mode 100644 index 0000000000000..defb5dbe52e64 --- /dev/null +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTranslationTest.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Extension\Core\CoreExtension; +use Symfony\Component\Form\Test\TypeTestCase; +use Symfony\Contracts\Translation\TranslatorInterface; + +class ChoiceTypeTranslationTest extends TypeTestCase +{ + public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\ChoiceType'; + + private $choices = [ + 'Bernhard' => 'a', + 'Fabien' => 'b', + 'Kris' => 'c', + 'Jon' => 'd', + 'Roman' => 'e', + ]; + + protected function getExtensions() + { + $translator = $this->createMock(TranslatorInterface::class); + $translator->expects($this->any())->method('trans') + ->willReturnCallback(function ($key, $params) { + return strtr(sprintf('Translation of: %s', $key), $params); + } + ); + + return array_merge(parent::getExtensions(), [new CoreExtension(null, null, $translator)]); + } + + public function testInvalidMessageAwarenessForMultiple() + { + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'multiple' => true, + 'expanded' => false, + 'choices' => $this->choices, + 'invalid_message' => 'You are not able to use value "{{ value }}"', + ]); + + $form->submit(['My invalid choice']); + $this->assertEquals( + "ERROR: Translation of: You are not able to use value \"My invalid choice\"\n", + (string) $form->getErrors(true) + ); + } + + public function testInvalidMessageAwarenessForMultipleWithoutScalarOrArrayViewData() + { + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'multiple' => true, + 'expanded' => false, + 'choices' => $this->choices, + 'invalid_message' => 'You are not able to use value "{{ value }}"', + ]); + + $form->submit(new \stdClass()); + $this->assertEquals( + "ERROR: Translation of: You are not able to use value \"stdClass\"\n", + (string) $form->getErrors(true) + ); + } +} diff --git a/src/Symfony/Component/HttpClient/DependencyInjection/HttpClientPass.php b/src/Symfony/Component/HttpClient/DependencyInjection/HttpClientPass.php index 5ed88909eba67..73f88651345d3 100644 --- a/src/Symfony/Component/HttpClient/DependencyInjection/HttpClientPass.php +++ b/src/Symfony/Component/HttpClient/DependencyInjection/HttpClientPass.php @@ -42,6 +42,7 @@ public function process(ContainerBuilder $container) foreach ($container->findTaggedServiceIds($this->clientTag) as $id => $tags) { $container->register('.debug.'.$id, TraceableHttpClient::class) ->setArguments([new Reference('.debug.'.$id.'.inner'), new Reference('debug.stopwatch', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)]) + ->addTag('kernel.reset', ['method' => 'reset']) ->setDecoratedService($id); $container->getDefinition('data_collector.http_client') ->addMethodCall('registerClient', [$id, new Reference('.debug.'.$id)]); diff --git a/src/Symfony/Component/HttpClient/EventSourceHttpClient.php b/src/Symfony/Component/HttpClient/EventSourceHttpClient.php index 81d9f4bfc8bf8..7ac8940b6cada 100644 --- a/src/Symfony/Component/HttpClient/EventSourceHttpClient.php +++ b/src/Symfony/Component/HttpClient/EventSourceHttpClient.php @@ -78,7 +78,7 @@ public function request(string $method, string $url, array $options = []): Respo try { $isTimeout = $chunk->isTimeout(); - if (null !== $chunk->getInformationalStatus()) { + if (null !== $chunk->getInformationalStatus() || $context->getInfo('canceled')) { yield $chunk; return; diff --git a/src/Symfony/Component/HttpClient/Response/AsyncResponse.php b/src/Symfony/Component/HttpClient/Response/AsyncResponse.php index 06518f1a2cb3a..3d07cba9b9b43 100644 --- a/src/Symfony/Component/HttpClient/Response/AsyncResponse.php +++ b/src/Symfony/Component/HttpClient/Response/AsyncResponse.php @@ -247,7 +247,7 @@ public static function stream(iterable $responses, float $timeout = null, string } } - if (!$client) { + if (!$client || !$wrappedResponses) { return; } diff --git a/src/Symfony/Component/HttpClient/Response/CurlResponse.php b/src/Symfony/Component/HttpClient/Response/CurlResponse.php index 9d289d84770fa..001c037394fd3 100644 --- a/src/Symfony/Component/HttpClient/Response/CurlResponse.php +++ b/src/Symfony/Component/HttpClient/Response/CurlResponse.php @@ -247,13 +247,15 @@ public function getContent(bool $throw = true): string public function __destruct() { - curl_setopt($this->handle, \CURLOPT_VERBOSE, false); + try { + if (null === $this->timeout) { + return; // Unused pushed response + } - if (null === $this->timeout) { - return; // Unused pushed response + $this->doDestruct(); + } finally { + curl_setopt($this->handle, \CURLOPT_VERBOSE, false); } - - $this->doDestruct(); } /** @@ -313,6 +315,10 @@ private static function perform(ClientState $multi, array &$responses = null): v } } + if (\CURLE_RECV_ERROR === $result && 'H' === $waitFor[0] && 400 <= ($responses[(int) $ch]->info['http_code'] ?? 0)) { + $multi->handlesActivity[$id][] = new FirstChunk(); + } + $multi->handlesActivity[$id][] = null; $multi->handlesActivity[$id][] = \in_array($result, [\CURLE_OK, \CURLE_TOO_MANY_REDIRECTS], true) || '_0' === $waitFor || curl_getinfo($ch, \CURLINFO_SIZE_DOWNLOAD) === curl_getinfo($ch, \CURLINFO_CONTENT_LENGTH_DOWNLOAD) ? null : new TransportException(sprintf('%s for "%s".', curl_strerror($result), curl_getinfo($ch, \CURLINFO_EFFECTIVE_URL))); } diff --git a/src/Symfony/Component/HttpClient/Response/MockResponse.php b/src/Symfony/Component/HttpClient/Response/MockResponse.php index bdb655926628f..71fe8fbd17592 100644 --- a/src/Symfony/Component/HttpClient/Response/MockResponse.php +++ b/src/Symfony/Component/HttpClient/Response/MockResponse.php @@ -105,7 +105,11 @@ public function cancel(): void { $this->info['canceled'] = true; $this->info['error'] = 'Response has been canceled.'; - $this->body = null; + try { + $this->body = null; + } catch (TransportException $e) { + // ignore errors when canceling + } } /** diff --git a/src/Symfony/Component/HttpClient/RetryableHttpClient.php b/src/Symfony/Component/HttpClient/RetryableHttpClient.php index afab2f8d0388b..97b48da423a85 100644 --- a/src/Symfony/Component/HttpClient/RetryableHttpClient.php +++ b/src/Symfony/Component/HttpClient/RetryableHttpClient.php @@ -59,7 +59,7 @@ public function request(string $method, string $url, array $options = []): Respo return new AsyncResponse($this->client, $method, $url, $options, function (ChunkInterface $chunk, AsyncContext $context) use ($method, $url, $options, &$retryCount, &$content, &$firstChunk) { $exception = null; try { - if ($chunk->isTimeout() || null !== $chunk->getInformationalStatus()) { + if ($chunk->isTimeout() || null !== $chunk->getInformationalStatus() || $context->getInfo('canceled')) { yield $chunk; return; @@ -76,23 +76,14 @@ public function request(string $method, string $url, array $options = []): Respo } if (false === $shouldRetry) { - $context->passthru(); - if (null !== $firstChunk) { - yield $firstChunk; - yield $context->createChunk($content); - yield $chunk; - } else { - yield $chunk; - } - $content = ''; + yield from $this->passthru($context, $firstChunk, $content, $chunk); return; } } } elseif ($chunk->isFirst()) { if (false === $shouldRetry = $this->strategy->shouldRetry($context, null, null)) { - $context->passthru(); - yield $chunk; + yield from $this->passthru($context, $firstChunk, $content, $chunk); return; } @@ -105,9 +96,9 @@ public function request(string $method, string $url, array $options = []): Respo return; } } else { - $content .= $chunk->getContent(); - if (!$chunk->isLast()) { + $content .= $chunk->getContent(); + return; } @@ -116,10 +107,7 @@ public function request(string $method, string $url, array $options = []): Respo } if (false === $shouldRetry) { - $context->passthru(); - yield $firstChunk; - yield $context->createChunk($content); - $content = ''; + yield from $this->passthru($context, $firstChunk, $content, $chunk); return; } @@ -159,4 +147,22 @@ private function getDelayFromHeader(array $headers): ?int return null; } + + private function passthru(AsyncContext $context, ?ChunkInterface $firstChunk, string &$content, ChunkInterface $lastChunk): \Generator + { + $context->passthru(); + + if (null !== $firstChunk) { + yield $firstChunk; + } + + if ('' !== $content) { + $chunk = $context->createChunk($content); + $content = ''; + + yield $chunk; + } + + yield $lastChunk; + } } diff --git a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php index 87a460741e334..59e4dc1da7cc8 100644 --- a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php +++ b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php @@ -361,4 +361,16 @@ public function testHandleIsRemovedOnException() $this->assertCount(0, $clientState->openHandles); } } + + public function testDebugInfoOnDestruct() + { + $client = $this->getHttpClient(__FUNCTION__); + + $traceInfo = []; + $client->request('GET', 'http://localhost:8057', ['on_progress' => function (int $dlNow, int $dlSize, array $info) use (&$traceInfo) { + $traceInfo = $info; + }]); + + $this->assertNotEmpty($traceInfo['debug']); + } } diff --git a/src/Symfony/Component/HttpClient/Tests/RetryableHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/RetryableHttpClientTest.php index e088ad03ffb84..415eb41d51ad6 100644 --- a/src/Symfony/Component/HttpClient/Tests/RetryableHttpClientTest.php +++ b/src/Symfony/Component/HttpClient/Tests/RetryableHttpClientTest.php @@ -4,6 +4,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\HttpClient\Exception\ServerException; +use Symfony\Component\HttpClient\HttpClient; use Symfony\Component\HttpClient\MockHttpClient; use Symfony\Component\HttpClient\NativeHttpClient; use Symfony\Component\HttpClient\Response\AsyncContext; @@ -159,4 +160,22 @@ public function shouldRetry(AsyncContext $context, ?string $responseContent, ?Tr $this->assertCount(2, $logger->logs); $this->assertSame('Try #{count} after {delay}ms: Could not resolve host "does.not.exists".', $logger->logs[0]); } + + public function testCancelOnTimeout() + { + $client = HttpClient::create(); + + if ($client instanceof NativeHttpClient) { + $this->markTestSkipped('NativeHttpClient cannot timeout before receiving headers'); + } + + $client = new RetryableHttpClient($client); + + $response = $client->request('GET', 'https://example.com/'); + + foreach ($client->stream($response, 0) as $chunk) { + $this->assertTrue($chunk->isTimeout()); + $response->cancel(); + } + } } diff --git a/src/Symfony/Component/HttpFoundation/IpUtils.php b/src/Symfony/Component/HttpFoundation/IpUtils.php index 68426f5b0bddc..6d98e5cd35bc3 100644 --- a/src/Symfony/Component/HttpFoundation/IpUtils.php +++ b/src/Symfony/Component/HttpFoundation/IpUtils.php @@ -36,6 +36,10 @@ private function __construct() */ public static function checkIp(?string $requestIp, $ips) { + if (null === $requestIp) { + return false; + } + if (!\is_array($ips)) { $ips = [$ips]; } diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index 6393cc2790d62..853b81fbea318 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -138,7 +138,7 @@ class Response * * The list of codes is complete according to the * {@link https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml Hypertext Transfer Protocol (HTTP) Status Code Registry} - * (last updated 2016-03-01). + * (last updated 2018-09-21). * * Unless otherwise noted, the status code is defined in RFC2616. * diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/xml/http-status-codes.xml b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/xml/http-status-codes.xml new file mode 100644 index 0000000000000..9e506696a4ca4 --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/xml/http-status-codes.xml @@ -0,0 +1,375 @@ + + + + + Hypertext Transfer Protocol (HTTP) Status Code Registry + 2018-09-21 + + HTTP Status Codes + + IETF Review + 1xx: Informational - Request received, continuing process + 2xx: Success - The action was successfully received, understood, and accepted + 3xx: Redirection - Further action must be taken in order to complete the request + 4xx: Client Error - The request contains bad syntax or cannot be fulfilled + 5xx: Server Error - The server failed to fulfill an apparently valid request + + + 100 + Continue + RFC7231, Section 6.2.1 + + + 101 + Switching Protocols + RFC7231, Section 6.2.2 + + + 102 + Processing + + + + 103 + Early Hints + + + + 104-199 + Unassigned + + + 200 + OK + RFC7231, Section 6.3.1 + + + 201 + Created + RFC7231, Section 6.3.2 + + + 202 + Accepted + RFC7231, Section 6.3.3 + + + 203 + Non-Authoritative Information + RFC7231, Section 6.3.4 + + + 204 + No Content + RFC7231, Section 6.3.5 + + + 205 + Reset Content + RFC7231, Section 6.3.6 + + + 206 + Partial Content + RFC7233, Section 4.1 + + + 207 + Multi-Status + + + + 208 + Already Reported + + + + 209-225 + Unassigned + + + 226 + IM Used + + + + 227-299 + Unassigned + + + 300 + Multiple Choices + RFC7231, Section 6.4.1 + + + 301 + Moved Permanently + RFC7231, Section 6.4.2 + + + 302 + Found + RFC7231, Section 6.4.3 + + + 303 + See Other + RFC7231, Section 6.4.4 + + + 304 + Not Modified + RFC7232, Section 4.1 + + + 305 + Use Proxy + RFC7231, Section 6.4.5 + + + 306 + (Unused) + RFC7231, Section 6.4.6 + + + 307 + Temporary Redirect + RFC7231, Section 6.4.7 + + + 308 + Permanent Redirect + + + + 309-399 + Unassigned + + + 400 + Bad Request + RFC7231, Section 6.5.1 + + + 401 + Unauthorized + RFC7235, Section 3.1 + + + 402 + Payment Required + RFC7231, Section 6.5.2 + + + 403 + Forbidden + RFC7231, Section 6.5.3 + + + 404 + Not Found + RFC7231, Section 6.5.4 + + + 405 + Method Not Allowed + RFC7231, Section 6.5.5 + + + 406 + Not Acceptable + RFC7231, Section 6.5.6 + + + 407 + Proxy Authentication Required + RFC7235, Section 3.2 + + + 408 + Request Timeout + RFC7231, Section 6.5.7 + + + 409 + Conflict + RFC7231, Section 6.5.8 + + + 410 + Gone + RFC7231, Section 6.5.9 + + + 411 + Length Required + RFC7231, Section 6.5.10 + + + 412 + Precondition Failed + RFC7232, Section 4.2 + RFC8144, Section 3.2 + + + 413 + Payload Too Large + RFC7231, Section 6.5.11 + + + 414 + URI Too Long + RFC7231, Section 6.5.12 + + + 415 + Unsupported Media Type + RFC7231, Section 6.5.13 + RFC7694, Section 3 + + + 416 + Range Not Satisfiable + RFC7233, Section 4.4 + + + 417 + Expectation Failed + RFC7231, Section 6.5.14 + + + 418-420 + Unassigned + + + 421 + Misdirected Request + RFC7540, Section 9.1.2 + + + 422 + Unprocessable Entity + + + + 423 + Locked + + + + 424 + Failed Dependency + + + + 425 + Too Early + + + + 426 + Upgrade Required + RFC7231, Section 6.5.15 + + + 427 + Unassigned + + + 428 + Precondition Required + + + + 429 + Too Many Requests + + + + 430 + Unassigned + + + 431 + Request Header Fields Too Large + + + + 432-450 + Unassigned + + + 451 + Unavailable For Legal Reasons + + + + 452-499 + Unassigned + + + 500 + Internal Server Error + RFC7231, Section 6.6.1 + + + 501 + Not Implemented + RFC7231, Section 6.6.2 + + + 502 + Bad Gateway + RFC7231, Section 6.6.3 + + + 503 + Service Unavailable + RFC7231, Section 6.6.4 + + + 504 + Gateway Timeout + RFC7231, Section 6.6.5 + + + 505 + HTTP Version Not Supported + RFC7231, Section 6.6.6 + + + 506 + Variant Also Negotiates + + + + 507 + Insufficient Storage + + + + 508 + Loop Detected + + + + 509 + Unassigned + + + 510 + Not Extended + + + + 511 + Network Authentication Required + + + + 512-599 + Unassigned + + + + diff --git a/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php b/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php index 2510b830a17d1..48509f9667cd7 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php @@ -39,6 +39,8 @@ public function getIpv4Data() [true, '1.2.3.4', '192.168.1.0/0'], [false, '1.2.3.4', '256.256.256/0'], // invalid CIDR notation [false, 'an_invalid_ip', '192.168.1.0/24'], + [false, '', '1.2.3.4/1'], + [false, null, '1.2.3.4/1'], ]; } @@ -69,6 +71,8 @@ public function getIpv6Data() [false, '2a01:198:603:0:396e:4789:8e99:890f', ['::1', '1a01:198:603:0::/65']], [false, '}__test|O:21:"JDatabaseDriverMysqli":3:{s:2', '::1'], [false, '2a01:198:603:0:396e:4789:8e99:890f', 'unknown'], + [false, '', '::1'], + [false, null, '::1'], ]; } diff --git a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php index c44ecc1f6099b..8e15f7a664af4 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php @@ -1050,25 +1050,10 @@ protected function provideResponse() */ public function ianaCodesReasonPhrasesProvider() { - if (!\in_array('https', stream_get_wrappers(), true)) { - $this->markTestSkipped('The "https" wrapper is not available'); - } - + // XML taken from https://www.iana.org/assignments/http-status-codes/http-status-codes.xml + // (might not be up-to-date for older Symfony versions) $ianaHttpStatusCodes = new \DOMDocument(); - - $context = stream_context_create([ - 'http' => [ - 'method' => 'GET', - 'timeout' => 30, - 'user_agent' => __METHOD__, - ], - ]); - - if (!$rawStatusCodes = file_get_contents('https://www.iana.org/assignments/http-status-codes/http-status-codes.xml', false, $context)) { - $this->markTestSkipped('The IANA server is throttling the list of status codes'); - } - - $ianaHttpStatusCodes->loadXML($rawStatusCodes); + $ianaHttpStatusCodes->load(__DIR__.'/Fixtures/xml/http-status-codes.xml'); if (!$ianaHttpStatusCodes->relaxNGValidate(__DIR__.'/schema/http-status-codes.rng')) { self::fail('Invalid IANA\'s HTTP status code list.'); } diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 7645f3df37e69..ca588639cb3cc 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -75,11 +75,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private static $freshCache = []; - public const VERSION = '5.3.9'; - public const VERSION_ID = 50309; + public const VERSION = '5.3.10'; + public const VERSION_ID = 50310; public const MAJOR_VERSION = 5; public const MINOR_VERSION = 3; - public const RELEASE_VERSION = 9; + public const RELEASE_VERSION = 10; public const EXTRA_VERSION = ''; public const END_OF_MAINTENANCE = '01/2022'; diff --git a/src/Symfony/Component/HttpKernel/Tests/Fragment/FragmentHandlerTest.php b/src/Symfony/Component/HttpKernel/Tests/Fragment/FragmentHandlerTest.php index b78fba4ce477f..1d0eb90bf6fdb 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fragment/FragmentHandlerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fragment/FragmentHandlerTest.php @@ -19,9 +19,6 @@ use Symfony\Component\HttpKernel\Fragment\FragmentHandler; use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface; -/** - * @group time-sensitive - */ class FragmentHandlerTest extends TestCase { private $requestStack; @@ -71,7 +68,20 @@ public function testDeliverWithUnsuccessfulResponse() public function testRender() { - $handler = $this->getHandler($this->returnValue(new Response('foo')), ['/', Request::create('/'), ['foo' => 'foo', 'ignore_errors' => true]]); + $expectedRequest = Request::create('/'); + $handler = $this->getHandler( + $this->returnValue(new Response('foo')), + [ + '/', + $this->callback(function (Request $request) use ($expectedRequest) { + $expectedRequest->server->remove('REQUEST_TIME_FLOAT'); + $request->server->remove('REQUEST_TIME_FLOAT'); + + return $expectedRequest == $request; + }), + ['foo' => 'foo', 'ignore_errors' => true], + ] + ); $this->assertEquals('foo', $handler->render('/', 'foo', ['foo' => 'foo'])); } diff --git a/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php b/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php index acd9df73753af..c22a426d7d31e 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php @@ -261,13 +261,18 @@ public function testIpAddressOfRangedTrustedProxyIsSetAsRemote() /** * Creates a Kernel expecting a request equals to $request. */ - private function getKernelExpectingRequest(Request $request, $strict = false) + private function getKernelExpectingRequest(Request $expectedRequest) { $kernel = $this->createMock(HttpKernelInterface::class); $kernel ->expects($this->once()) ->method('handle') - ->with($request) + ->with($this->callback(function (Request $request) use ($expectedRequest) { + $expectedRequest->server->remove('REQUEST_TIME_FLOAT'); + $request->server->remove('REQUEST_TIME_FLOAT'); + + return $expectedRequest == $request; + })) ->willReturn(new Response('foo')); return $kernel; diff --git a/src/Symfony/Component/Lock/Store/PdoStore.php b/src/Symfony/Component/Lock/Store/PdoStore.php index b300e9fff3a5e..20673a7342fc7 100644 --- a/src/Symfony/Component/Lock/Store/PdoStore.php +++ b/src/Symfony/Component/Lock/Store/PdoStore.php @@ -272,7 +272,7 @@ public function createTable(): void $this->addTableToSchema($schema); foreach ($schema->toSql($conn->getDatabasePlatform()) as $sql) { - if (method_exists($conn, 'executeStatement')) { + if ($conn instanceof Connection && method_exists($conn, 'executeStatement')) { $conn->executeStatement($sql); } else { $conn->exec($sql); @@ -302,7 +302,7 @@ public function createTable(): void throw new \DomainException(sprintf('Creating the lock table is currently not implemented for PDO driver "%s".', $driver)); } - if (method_exists($conn, 'executeStatement')) { + if ($conn instanceof Connection && method_exists($conn, 'executeStatement')) { $conn->executeStatement($sql); } else { $conn->exec($sql); @@ -333,7 +333,7 @@ private function prune(): void $sql = "DELETE FROM $this->table WHERE $this->expirationCol <= {$this->getCurrentTimestampStatement()}"; $conn = $this->getConnection(); - if (method_exists($conn, 'executeStatement')) { + if ($conn instanceof Connection && method_exists($conn, 'executeStatement')) { $conn->executeStatement($sql); } else { $conn->exec($sql); diff --git a/src/Symfony/Component/Lock/Store/PostgreSqlStore.php b/src/Symfony/Component/Lock/Store/PostgreSqlStore.php index 9fdbe94bac2ba..0e472d2c82717 100644 --- a/src/Symfony/Component/Lock/Store/PostgreSqlStore.php +++ b/src/Symfony/Component/Lock/Store/PostgreSqlStore.php @@ -18,7 +18,7 @@ use Symfony\Component\Lock\Exception\InvalidArgumentException; use Symfony\Component\Lock\Exception\LockConflictedException; use Symfony\Component\Lock\Key; -use Symfony\Component\Lock\PersistingStoreInterface; +use Symfony\Component\Lock\SharedLockStoreInterface; /** * PostgreSqlStore is a PersistingStoreInterface implementation using @@ -276,7 +276,7 @@ private function checkDriver(): void } } - private function getInternalStore(): PersistingStoreInterface + private function getInternalStore(): SharedLockStoreInterface { $namespace = spl_object_hash($this->getConnection()); diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/PostgreSqlConnection.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/PostgreSqlConnection.php index 34fa328486e7a..d59e7b52c15ed 100644 --- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/PostgreSqlConnection.php +++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/PostgreSqlConnection.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Messenger\Bridge\Doctrine\Transport; +use Doctrine\DBAL\Driver\PDO\Connection as DoctrinePdoConnection; use Doctrine\DBAL\Schema\Table; /** @@ -72,7 +73,12 @@ public function get(): ?array $this->listening = true; } - $notification = $this->driverConnection->getWrappedConnection()->pgsqlGetNotify(\PDO::FETCH_ASSOC, $this->configuration['get_notify_timeout']); + $wrappedConnection = $this->driverConnection->getWrappedConnection(); + if (!$wrappedConnection instanceof \PDO && $wrappedConnection instanceof DoctrinePdoConnection) { + $wrappedConnection = $wrappedConnection->getWrappedConnection(); + } + + $notification = $wrappedConnection->pgsqlGetNotify(\PDO::FETCH_ASSOC, $this->configuration['get_notify_timeout']); if ( // no notifications, or for another table or queue (false === $notification || $notification['message'] !== $this->configuration['table_name'] || $notification['payload'] !== $this->configuration['queue_name']) && diff --git a/src/Symfony/Component/Messenger/Bridge/Redis/Tests/Transport/ConnectionTest.php b/src/Symfony/Component/Messenger/Bridge/Redis/Tests/Transport/ConnectionTest.php index 1754c83da008c..ae71c08e6c617 100644 --- a/src/Symfony/Component/Messenger/Bridge/Redis/Tests/Transport/ConnectionTest.php +++ b/src/Symfony/Component/Messenger/Bridge/Redis/Tests/Transport/ConnectionTest.php @@ -155,7 +155,7 @@ public function testKeepGettingPendingMessages() $redis->expects($this->exactly(3))->method('xreadgroup') ->with('symfony', 'consumer', ['queue' => 0], 1, null) - ->willReturn(['queue' => [['message' => '{"body":"Test","headers":[]}']]]); + ->willReturn(['queue' => [['message' => json_encode(['body' => 'Test', 'headers' => []])]]]); $connection = Connection::fromDsn('redis://localhost/queue', [], $redis); $this->assertNotNull($connection->get()); @@ -183,6 +183,9 @@ public function provideAuthDsn(): \Generator { yield 'Password only' => ['password', 'redis://password@localhost/queue']; yield 'User and password' => [['user', 'password'], 'redis://user:password@localhost/queue']; + yield 'User and colon' => ['user', 'redis://user:@localhost/queue']; + yield 'Colon and password' => ['password', 'redis://:password@localhost/queue']; + yield 'Colon and falsy password' => ['0', 'redis://:0@localhost/queue']; } public function testAuthFromOptions() diff --git a/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php b/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php index 7ffd04caa97f5..bfb712dbf3efe 100644 --- a/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php +++ b/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php @@ -236,11 +236,13 @@ public static function fromDsn(string $dsn, array $redisOptions = [], $redis = n ]; if (isset($parsedUrl['host'])) { + $pass = '' !== ($parsedUrl['pass'] ?? '') ? $parsedUrl['pass'] : null; + $user = '' !== ($parsedUrl['user'] ?? '') ? $parsedUrl['user'] : null; $connectionCredentials = [ 'host' => $parsedUrl['host'] ?? '127.0.0.1', 'port' => $parsedUrl['port'] ?? 6379, // See: https://github.com/phpredis/phpredis/#auth - 'auth' => $redisOptions['auth'] ?? (isset($parsedUrl['pass']) && isset($parsedUrl['user']) ? [$parsedUrl['user'], $parsedUrl['pass']] : $parsedUrl['pass'] ?? $parsedUrl['user'] ?? null), + 'auth' => $redisOptions['auth'] ?? (null !== $pass && null !== $user ? [$user, $pass] : ($pass ?? $user)), ]; $pathParts = explode('/', rtrim($parsedUrl['path'] ?? '', '/')); diff --git a/src/Symfony/Component/Messenger/Tests/TraceableMessageBusTest.php b/src/Symfony/Component/Messenger/Tests/TraceableMessageBusTest.php index d0b7db99e0c9d..0b57cf37bad6c 100644 --- a/src/Symfony/Component/Messenger/Tests/TraceableMessageBusTest.php +++ b/src/Symfony/Component/Messenger/Tests/TraceableMessageBusTest.php @@ -156,4 +156,22 @@ public function testItTracesExceptions() ], ], $actualTracedMessage); } + + public function testItTracesExceptionsWhenMessageBusIsFiredFromArrayCallback() + { + $message = new DummyMessage('Hello'); + $exception = new \RuntimeException(); + + $bus = $this->createMock(MessageBusInterface::class); + $bus->expects($this->once()) + ->method('dispatch') + ->with($message) + ->willThrowException($exception); + + $traceableBus = new TraceableMessageBus($bus); + + $this->expectExceptionObject($exception); + + array_map([$traceableBus, 'dispatch'], [$message]); + } } diff --git a/src/Symfony/Component/Messenger/Tests/Transport/Serialization/PhpSerializerTest.php b/src/Symfony/Component/Messenger/Tests/Transport/Serialization/PhpSerializerTest.php index 96f4503c2eede..891683790ad47 100644 --- a/src/Symfony/Component/Messenger/Tests/Transport/Serialization/PhpSerializerTest.php +++ b/src/Symfony/Component/Messenger/Tests/Transport/Serialization/PhpSerializerTest.php @@ -53,6 +53,18 @@ public function testDecodingFailsWithBadFormat() ]); } + public function testDecodingFailsWithBadBase64Body() + { + $this->expectException(MessageDecodingFailedException::class); + $this->expectExceptionMessageMatches('/Could not decode/'); + + $serializer = new PhpSerializer(); + + $serializer->decode([ + 'body' => 'x', + ]); + } + public function testDecodingFailsWithBadClass() { $this->expectException(MessageDecodingFailedException::class); diff --git a/src/Symfony/Component/Messenger/TraceableMessageBus.php b/src/Symfony/Component/Messenger/TraceableMessageBus.php index b6784af706c57..df595b0867fa5 100644 --- a/src/Symfony/Component/Messenger/TraceableMessageBus.php +++ b/src/Symfony/Component/Messenger/TraceableMessageBus.php @@ -62,8 +62,8 @@ private function getCaller(): array { $trace = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 8); - $file = $trace[1]['file']; - $line = $trace[1]['line']; + $file = $trace[1]['file'] ?? null; + $line = $trace[1]['line'] ?? null; $handleTraitFile = (new \ReflectionClass(HandleTrait::class))->getFileName(); $found = false; @@ -97,9 +97,12 @@ private function getCaller(): array } } - $name = str_replace('\\', '/', $file); - $name = substr($name, strrpos($name, '/') + 1); + $name = str_replace('\\', '/', (string) $file); - return compact('name', 'file', 'line'); + return [ + 'name' => substr($name, strrpos($name, '/') + 1), + 'file' => $file, + 'line' => $line, + ]; } } diff --git a/src/Symfony/Component/Messenger/Transport/Serialization/PhpSerializer.php b/src/Symfony/Component/Messenger/Transport/Serialization/PhpSerializer.php index 3a4804903c673..0516ee2f40292 100644 --- a/src/Symfony/Component/Messenger/Transport/Serialization/PhpSerializer.php +++ b/src/Symfony/Component/Messenger/Transport/Serialization/PhpSerializer.php @@ -58,6 +58,10 @@ public function encode(Envelope $envelope): array private function safelyUnserialize(string $contents) { + if ('' === $contents) { + throw new MessageDecodingFailedException('Could not decode an empty message using PHP serialization.'); + } + $signalingException = new MessageDecodingFailedException(sprintf('Could not decode message using PHP serialization: %s.', $contents)); $prevUnserializeHandler = ini_set('unserialize_callback_func', self::class.'::handleUnserializeCallback'); $prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = []) use (&$prevErrorHandler, $signalingException) { diff --git a/src/Symfony/Component/Notifier/Bridge/Firebase/FirebaseTransport.php b/src/Symfony/Component/Notifier/Bridge/Firebase/FirebaseTransport.php index 526ad5eb6ac85..70cce2b680c3c 100644 --- a/src/Symfony/Component/Notifier/Bridge/Firebase/FirebaseTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/Firebase/FirebaseTransport.php @@ -83,15 +83,17 @@ protected function doSend(MessageInterface $message): SentMessage $contentType = $response->getHeaders(false)['content-type'][0] ?? ''; $jsonContents = 0 === strpos($contentType, 'application/json') ? $response->toArray(false) : null; + $errorMessage = null; - if (200 !== $statusCode) { - $errorMessage = $jsonContents ? $jsonContents['results']['error'] : $response->getContent(false); + if ($jsonContents && isset($jsonContents['results'][0]['error'])) { + $errorMessage = $jsonContents['results'][0]['error']; + } elseif (200 !== $statusCode) { + $errorMessage = $response->getContent(false); + } + if (null !== $errorMessage) { throw new TransportException('Unable to post the Firebase message: '.$errorMessage, $response); } - if ($jsonContents && isset($jsonContents['results'][0]['error'])) { - throw new TransportException('Unable to post the Firebase message: '.$jsonContents['results'][0]['error'], $response); - } $success = $response->toArray(false); diff --git a/src/Symfony/Component/Notifier/Bridge/Firebase/Tests/FirebaseTransportTest.php b/src/Symfony/Component/Notifier/Bridge/Firebase/Tests/FirebaseTransportTest.php index 7a28b4f32f5df..5f8d52aa6440a 100644 --- a/src/Symfony/Component/Notifier/Bridge/Firebase/Tests/FirebaseTransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/Firebase/Tests/FirebaseTransportTest.php @@ -11,13 +11,18 @@ namespace Symfony\Component\Notifier\Bridge\Firebase\Tests; +use Symfony\Component\HttpClient\MockHttpClient; +use Symfony\Component\HttpClient\Response\MockResponse; +use Symfony\Component\Notifier\Bridge\Firebase\FirebaseOptions; use Symfony\Component\Notifier\Bridge\Firebase\FirebaseTransport; +use Symfony\Component\Notifier\Exception\TransportException; use Symfony\Component\Notifier\Message\ChatMessage; use Symfony\Component\Notifier\Message\MessageInterface; use Symfony\Component\Notifier\Message\SmsMessage; use Symfony\Component\Notifier\Test\TransportTestCase; use Symfony\Component\Notifier\Transport\TransportInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; /** * @author Oskar Stark @@ -47,4 +52,34 @@ public function unsupportedMessagesProvider(): iterable yield [new SmsMessage('0611223344', 'Hello!')]; yield [$this->createMock(MessageInterface::class)]; } + + /** + * @dataProvider sendWithErrorThrowsExceptionProvider + */ + public function testSendWithErrorThrowsTransportException(ResponseInterface $response) + { + $this->expectException(TransportException::class); + + $client = new MockHttpClient(static function () use ($response): ResponseInterface { + return $response; + }); + $options = new class('recipient-id', []) extends FirebaseOptions {}; + + $transport = $this->createTransport($client); + + $transport->send(new ChatMessage('Hello!', $options)); + } + + public function sendWithErrorThrowsExceptionProvider(): iterable + { + yield [new MockResponse( + json_encode(['results' => [['error' => 'testErrorCode']]]), + ['response_headers' => ['content-type' => ['application/json']], 'http_code' => 200] + )]; + + yield [new MockResponse( + json_encode(['results' => [['error' => 'testErrorCode']]]), + ['response_headers' => ['content-type' => ['application/json']], 'http_code' => 400] + )]; + } } diff --git a/src/Symfony/Component/Notifier/Bridge/RocketChat/RocketChatTransport.php b/src/Symfony/Component/Notifier/Bridge/RocketChat/RocketChatTransport.php index 891d12752bc46..925dcb78a81e9 100644 --- a/src/Symfony/Component/Notifier/Bridge/RocketChat/RocketChatTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/RocketChat/RocketChatTransport.php @@ -99,7 +99,10 @@ protected function doSend(MessageInterface $message): SentMessage $success = $response->toArray(false); $sentMessage = new SentMessage($message, (string) $this); - $sentMessage->setMessageId($success['message']['_id']); + + if (isset($success['message']['_id'])) { + $sentMessage->setMessageId($success['message']['_id']); + } return $sentMessage; } diff --git a/src/Symfony/Component/Runtime/Internal/ComposerPlugin.php b/src/Symfony/Component/Runtime/Internal/ComposerPlugin.php index 77c48b3f0b380..8247b858e66b5 100644 --- a/src/Symfony/Component/Runtime/Internal/ComposerPlugin.php +++ b/src/Symfony/Component/Runtime/Internal/ComposerPlugin.php @@ -18,7 +18,6 @@ use Composer\Plugin\PluginInterface; use Composer\Script\ScriptEvents; use Symfony\Component\Filesystem\Filesystem; -use Symfony\Component\Runtime\RuntimeInterface; use Symfony\Component\Runtime\SymfonyRuntime; /** @@ -98,10 +97,6 @@ public function updateAutoloadFile(): void $runtimeClass = $extra['class'] ?? SymfonyRuntime::class; - if (SymfonyRuntime::class !== $runtimeClass && !is_subclass_of($runtimeClass, RuntimeInterface::class)) { - throw new \InvalidArgumentException(sprintf('Class "%s" listed under "extra.runtime.class" in your composer.json file '.(class_exists($runtimeClass) ? 'should implement "%s".' : 'not found.'), $runtimeClass, RuntimeInterface::class)); - } - unset($extra['class'], $extra['autoload_template']); $code = strtr(file_get_contents($autoloadTemplate), [ diff --git a/src/Symfony/Component/Runtime/Tests/phpt/command_list.phpt b/src/Symfony/Component/Runtime/Tests/phpt/command_list.phpt index 0383b35871660..ff1b6b3e06474 100644 --- a/src/Symfony/Component/Runtime/Tests/phpt/command_list.phpt +++ b/src/Symfony/Component/Runtime/Tests/phpt/command_list.phpt @@ -32,7 +32,7 @@ Options: --no-debug Switches off debug mode. -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug -Available commands: +Available commands:%A help Display%S help for a command list List%S commands my_command Hello description diff --git a/src/Symfony/Component/Security/CHANGELOG.md b/src/Symfony/Component/Security/CHANGELOG.md index 7dff040837c0d..c80d2dde00f29 100644 --- a/src/Symfony/Component/Security/CHANGELOG.md +++ b/src/Symfony/Component/Security/CHANGELOG.md @@ -38,7 +38,6 @@ The CHANGELOG for version 5.4 and newer can be found in the security sub-package * Add `LegacyPasswordAuthenticatedUserInterface` for user classes that use user-provided salts in addition to passwords * Deprecate all classes in the `Core\Encoder\` sub-namespace, use the `PasswordHasher` component instead * Deprecate the `SessionInterface $session` constructor argument of `SessionTokenStorage`, inject a `\Symfony\Component\HttpFoundation\RequestStack $requestStack` instead - * Deprecate using `UsageTrackingTokenStorage` without a main request * Deprecate the `session` service provided by the ServiceLocator injected in `UsageTrackingTokenStorage`, provide a `request_stack` service instead * Deprecate using `SessionTokenStorage` outside a request context, it will throw a `SessionNotFoundException` in Symfony 6.0 * Randomize CSRF tokens to harden BREACH attacks diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/Storage/UsageTrackingTokenStorage.php b/src/Symfony/Component/Security/Core/Authentication/Token/Storage/UsageTrackingTokenStorage.php index 4b2cac747abf5..27398059dd975 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Token/Storage/UsageTrackingTokenStorage.php +++ b/src/Symfony/Component/Security/Core/Authentication/Token/Storage/UsageTrackingTokenStorage.php @@ -101,8 +101,6 @@ private function shouldTrackUsage(): bool } if (!$this->container->get('request_stack')->getMainRequest()) { - trigger_deprecation('symfony/security-core', '5.3', 'Using "%s" (service ID: "security.token_storage") outside the request-response cycle is deprecated, use the "%s" class (service ID: "security.untracked_token_storage") instead or disable usage tracking using "disableUsageTracking()".', __CLASS__, TokenStorage::class); - return false; } diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.bs.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.bs.xlf index 2eae0ff22ec62..15fe823d8f911 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.bs.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.bs.xlf @@ -70,6 +70,14 @@ Invalid or expired login link. Link za prijavljivanje je istekao ili je neispravan. + + Too many failed login attempts, please try again in %minutes% minute. + Previše neuspjelih pokušaja prijave, pokušajte ponovo za %minutes% minuta. + + + Too many failed login attempts, please try again in %minutes% minutes. + Previše neuspjelih pokušaja prijave, pokušajte ponovo za %minutes% minuta. + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.da.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.da.xlf index c83d27c5e0b59..9b8ca4c68b2a3 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.da.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.da.xlf @@ -70,6 +70,14 @@ Invalid or expired login link. Ugyldigt eller udløbet login link. + + Too many failed login attempts, please try again in %minutes% minute. + For mange fejlede login forsøg, prøv igen om %minutes% minut. + + + Too many failed login attempts, please try again in %minutes% minutes. + For mange fejlede login forsøg, prøv igen om %minutes% minutter. + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.el.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.el.xlf index 724cc4e5d1603..1cf4fb23bdaf0 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.el.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.el.xlf @@ -44,7 +44,7 @@ Username could not be found. - Το Username δε βρέθηκε. + Το όνομα χρήστη δε βρέθηκε. Account has expired. @@ -70,6 +70,14 @@ Invalid or expired login link. Μη έγκυρος ή ληγμένος σύνδεσμος σύνδεσης. + + Too many failed login attempts, please try again in %minutes% minute. + Πολλαπλές αποτυχημένες απόπειρες σύνδεσης, παρακαλούμε ξαναδοκιμάστε σε %minutes% λεπτό. + + + Too many failed login attempts, please try again in %minutes% minutes. + Πολλαπλές αποτυχημένες απόπειρες σύνδεσης, παρακαλούμε ξαναδοκιμάστε σε %minutes% λεπτά. + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.fa.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.fa.xlf index dfa1995aa0e46..1127901bdfe5e 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.fa.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.fa.xlf @@ -32,7 +32,7 @@ No authentication provider found to support the authentication token. - هیچ ارایه دهنده احراز هویتی برای پشتیبانی از توکن احراز هویت پیدا نشد. + هیچ ارائه دهنده احراز هویتی برای پشتیبانی از توکن احراز هویت پیدا نشد. No session available, it either timed out or cookies are not enabled. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.gl.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.gl.xlf index 651810d452cb6..f552a6864665b 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.gl.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.gl.xlf @@ -70,6 +70,14 @@ Invalid or expired login link. Ligazón de inicio de sesión non válida ou caducada. + + Too many failed login attempts, please try again in %minutes% minute. + Demasiados intentos de inicio de sesión errados, por favor, ténteo de novo en %minutes% minuto. + + + Too many failed login attempts, please try again in %minutes% minutes. + Demasiados intentos de inicio de sesión errados, por favor, ténteo de novo en %minutes% minutos. + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.hy.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.hy.xlf index 459c292be31a6..e7e32020e9adb 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.hy.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.hy.xlf @@ -70,6 +70,14 @@ Invalid or expired login link. Անվավեր կամ ժամկետանց մուտքի հղում։ + + Too many failed login attempts, please try again in %minutes% minute. + Մուտքի չափազանց շատ անհաջող փորձեր: Խնդրում ենք կրկին փորձել %minutes րոպե: + + + Too many failed login attempts, please try again in %minutes% minutes. + Մուտքի չափազանց շատ անհաջող փորձեր: Խնդրում ենք կրկին փորձել %minutes րոպե: + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.lv.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.lv.xlf index 6c63276f4423f..bdb4a22357f4b 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.lv.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.lv.xlf @@ -70,6 +70,14 @@ Invalid or expired login link. Ieejas saite ir nederīga vai arī tai ir beidzies derīguma termiņš. + + Too many failed login attempts, please try again in %minutes% minute. + Pārāk daudz nesekmīgu autentifikācijas mēģinājumu, lūdzu mēģiniet vēlreiz pēc %minutes% minūtes. + + + Too many failed login attempts, please try again in %minutes% minutes. + Pārāk daudz nesekmīgu autentifikācijas mēģinājumu, lūdzu mēģiniet vēlreiz pēc %minutes% minūtēm. + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.sl.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.sl.xlf index 4d515e7ed7468..6466e58d5aada 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.sl.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.sl.xlf @@ -70,6 +70,14 @@ Invalid or expired login link. Neveljavna ali potekla povezava prijave. + + Too many failed login attempts, please try again in %minutes% minute. + Preveč neuspelih poskusov prijave, poskusite znova čez %minutes% minuto. + + + Too many failed login attempts, please try again in %minutes% minutes. + Preveč neuspelih poskusov prijave, poskusite znova čez %minutes% minut. + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.tl.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.tl.xlf index 66547b2a3d1be..eed0c7edf1875 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.tl.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.tl.xlf @@ -4,19 +4,19 @@ An authentication exception occurred. - Isang pambihirang pagpaptunay ang nangyari. + Nagkaroon ng isang pagbubukod sa pagpapatotoo. Authentication credentials could not be found. - Hindi mahanap ang mga kinakailangan na dokumento para sa pagpapatunay. + Hindi matagpuan ang mga kredensyal ng pagpapatotoo. Authentication request could not be processed due to a system problem. - Hindi maproseso ang iyong hiling dahil may problema sa sistema. + Ang kahilingan sa pagpapatotoo ay hindi naproseso dahil sa isang problema sa system. Invalid credentials. - Hindi balidong mga dokumento. + Di-wastong mga kredensyal. Cookie has already been used by someone else. @@ -28,31 +28,31 @@ Invalid CSRF token. - Hindi balidong mga token ng CSRF. + Di-wastong token ng CSRF. No authentication provider found to support the authentication token. - Walang nakitang nagbibibagay ng suporta sa token ng pagpapatunay. + Walang nahanap na provider ng pagpapatotoo upang suportahan ang token ng pagpapatotoo. No session available, it either timed out or cookies are not enabled. - Walang sesyon ng magagamit, ito ay nawalan ng oras o hindi pinagana. + Walang magagamit na session, alinman sa nag-time out o ang cookies ay hindi pinagana. No token could be found. - Walang token na nahanap. + Walang makitang token. Username could not be found. - Walang username na makita. + Hindi makita ang username. Account has expired. - Ang account ay nag-expire na. + Nag-expire na ang account. Credentials have expired. - Ang mga kinakailangang dokumento ay nag expire na. + Nag-expire na ang mga kredensyal. Account is disabled. @@ -60,11 +60,11 @@ Account is locked. - Ang account ay nakasara. + Ang account ay naka-lock. Too many failed login attempts, please try again later. - Madaming bagsak na pagtatangka, pakisubukan ulit mamaya. + Napakaraming nabigong mga pagtatangka sa pag-login, mangyaring subukang muli sa ibang pagkakataon. Invalid or expired login link. @@ -72,11 +72,11 @@ Too many failed login attempts, please try again in %minutes% minute. - Madaming bagsak na pagtatangka, pakisubukan ulit pagkatapos ng %minutes% minuto. + Napakaraming nabigong mga pagtatangka sa pag-login, pakisubukan ulit sa% minuto% minuto. Too many failed login attempts, please try again in %minutes% minute. - Madaming bagsak na pagtatangka, pakisubukan ulit pagkatapos ng %minutes% minuto. + Napakaraming nabigong mga pagtatangka sa pag-login, pakisubukan ulit sa% minuto% minuto. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.uz.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.uz.xlf index 4f031976c1aad..2b66d1be424ba 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.uz.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.uz.xlf @@ -70,6 +70,14 @@ Invalid or expired login link. Kirish havolasi yaroqsiz yoki muddati tugagan. + + Too many failed login attempts, please try again in %minutes% minute. + Kirish uchun muvaffaqiyatsiz urinishlar, %minutes% daqiqadan so'ng qayta urinib ko'ring. + + + Too many failed login attempts, please try again in %minutes% minutes. + Kirish uchun muvaffaqiyatsiz urinishlar, %minutes% daqiqadan so'ng qayta urinib ko'ring. + diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Token/Storage/UsageTrackingTokenStorageTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Token/Storage/UsageTrackingTokenStorageTest.php index 0d074bd4b040f..f8f11d1347289 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Token/Storage/UsageTrackingTokenStorageTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Token/Storage/UsageTrackingTokenStorageTest.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Session\SessionInterface; @@ -24,8 +23,6 @@ class UsageTrackingTokenStorageTest extends TestCase { - use ExpectDeprecationTrait; - public function testGetSetToken() { $sessionAccess = 0; @@ -69,9 +66,6 @@ public function testGetSetToken() $this->assertSame(1, $sessionAccess); } - /** - * @group legacy - */ public function testWithoutMainRequest() { $locator = new class(['request_stack' => function () { @@ -83,7 +77,6 @@ public function testWithoutMainRequest() $trackingStorage = new UsageTrackingTokenStorage($tokenStorage, $locator); $trackingStorage->enableUsageTracking(); - $this->expectDeprecation('Since symfony/security-core 5.3: Using "%s" (service ID: "security.token_storage") outside the request-response cycle is deprecated, use the "%s" class (service ID: "security.untracked_token_storage") instead or disable usage tracking using "disableUsageTracking()".'); - $trackingStorage->getToken(); + $this->assertNull($trackingStorage->getToken()); } } diff --git a/src/Symfony/Component/Security/Core/User/UserInterface.php b/src/Symfony/Component/Security/Core/User/UserInterface.php index f30d36e17d4d6..58f72a054d728 100644 --- a/src/Symfony/Component/Security/Core/User/UserInterface.php +++ b/src/Symfony/Component/Security/Core/User/UserInterface.php @@ -40,7 +40,7 @@ interface UserInterface * return ['ROLE_USER']; * } * - * Alternatively, the roles might be stored on a ``roles`` property, + * Alternatively, the roles might be stored in a ``roles`` property, * and populated in any number of different ways when the user object * is created. * diff --git a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php index 53e948eb36969..a688dba4e0286 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php @@ -248,8 +248,9 @@ protected function refreshUser(TokenInterface $token): ?TokenInterface $context = ['provider' => \get_class($provider), 'username' => method_exists($refreshedUser, 'getUserIdentifier') ? $refreshedUser->getUserIdentifier() : $refreshedUser->getUsername()]; if ($token instanceof SwitchUserToken) { - // @deprecated since Symfony 5.3, change to $token->getUserIdentifier() in 6.0 - $context['impersonator_username'] = method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getOriginalToken()->getUsername(); + $originalToken = $token->getOriginalToken(); + // @deprecated since Symfony 5.3, change to $originalToken->getUserIdentifier() in 6.0 + $context['impersonator_username'] = method_exists($originalToken, 'getUserIdentifier') ? $originalToken->getUserIdentifier() : $originalToken->getUsername(); } $this->logger->debug('User was reloaded from a user provider.', $context); diff --git a/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentRememberMeHandlerTest.php b/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentRememberMeHandlerTest.php index 00ce37b8dac6e..7448520497eaf 100644 --- a/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentRememberMeHandlerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentRememberMeHandlerTest.php @@ -124,7 +124,7 @@ public function testConsumeRememberMeCookieExpired() $this->tokenProvider->expects($this->any()) ->method('loadTokenBySeries') ->with('series1') - ->willReturn(new PersistentToken(InMemoryUser::class, 'wouter', 'series1', 'tokenvalue', new \DateTime('-'.(31536000 + 1).' seconds'))); + ->willReturn(new PersistentToken(InMemoryUser::class, 'wouter', 'series1', 'tokenvalue', new \DateTime('@'.(time() - (31536000 + 1))))); $this->tokenProvider->expects($this->never())->method('updateToken')->with('series1'); diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php index bd0f049c729f1..ebe8eefae7881 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php +++ b/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php @@ -33,12 +33,12 @@ class AnnotationLoader implements LoaderInterface { private const KNOWN_ANNOTATIONS = [ - DiscriminatorMap::class => true, - Groups::class => true, - Ignore::class => true, - MaxDepth::class => true, - SerializedName::class => true, - Context::class => true, + DiscriminatorMap::class, + Groups::class, + Ignore::class, + MaxDepth::class, + SerializedName::class, + Context::class, ]; private $reader; @@ -157,7 +157,7 @@ public function loadAnnotations(object $reflector): iterable { if (\PHP_VERSION_ID >= 80000) { foreach ($reflector->getAttributes() as $attribute) { - if (self::KNOWN_ANNOTATIONS[$attribute->getName()] ?? false) { + if ($this->isKnownAttribute($attribute->getName())) { yield $attribute->newInstance(); } } @@ -193,4 +193,15 @@ private function setAttributeContextsForGroups(Context $annotation, AttributeMet $attributeMetadata->setDenormalizationContextForGroups($annotation->getDenormalizationContext(), $annotation->getGroups()); } } + + private function isKnownAttribute(string $attributeName): bool + { + foreach (self::KNOWN_ANNOTATIONS as $knownAnnotation) { + if (is_a($attributeName, $knownAnnotation, true)) { + return true; + } + } + + return false; + } } diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 34e104f61d04f..7b46cd28fda15 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -422,11 +422,17 @@ private function validateAndDenormalize(string $currentClass, string $attribute, // if a value is meant to be a string, float, int or a boolean value from the serialized representation. // That's why we have to transform the values, if one of these non-string basic datatypes is expected. if (\is_string($data) && (XmlEncoder::FORMAT === $format || CsvEncoder::FORMAT === $format)) { - if ('' === $data && $type->isNullable() && \in_array($type->getBuiltinType(), [Type::BUILTIN_TYPE_BOOL, Type::BUILTIN_TYPE_INT, Type::BUILTIN_TYPE_FLOAT], true)) { - return null; + if ('' === $data) { + if (Type::BUILTIN_TYPE_ARRAY === $builtinType = $type->getBuiltinType()) { + return []; + } + + if ($type->isNullable() && \in_array($builtinType, [Type::BUILTIN_TYPE_BOOL, Type::BUILTIN_TYPE_INT, Type::BUILTIN_TYPE_FLOAT], true)) { + return null; + } } - switch ($type->getBuiltinType()) { + switch ($builtinType ?? $type->getBuiltinType()) { case Type::BUILTIN_TYPE_BOOL: // according to https://www.w3.org/TR/xmlschema-2/#boolean, valid representations are "false", "true", "0" and "1" if ('false' === $data || '0' === $data) { diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/Annotations/GroupDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/Annotations/GroupDummy.php index cf26b6e0da15d..1d502c60c5f86 100644 --- a/src/Symfony/Component/Serializer/Tests/Fixtures/Annotations/GroupDummy.php +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/Annotations/GroupDummy.php @@ -13,6 +13,7 @@ use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Serializer\Tests\Fixtures\GroupDummyInterface; +use Symfony\Component\Serializer\Tests\Fixtures\ChildOfGroupsAnnotationDummy; /** * @author Kévin Dunglas @@ -27,6 +28,11 @@ class GroupDummy extends GroupDummyParent implements GroupDummyInterface * @Groups({"b", "c", "name_converter"}) */ protected $bar; + /** + * @ChildOfGroupsAnnotationDummy + */ + protected $quux; + private $fooBar; private $symfony; @@ -78,4 +84,14 @@ public function getSymfony() { return $this->symfony; } + + public function getQuux() + { + return $this->quux; + } + + public function setQuux($quux): void + { + $this->quux = $quux; + } } diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/Attributes/GroupDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/Attributes/GroupDummy.php index eef1c55be9e1e..a056442095834 100644 --- a/src/Symfony/Component/Serializer/Tests/Fixtures/Attributes/GroupDummy.php +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/Attributes/GroupDummy.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Serializer\Tests\Fixtures\Attributes; use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Serializer\Tests\Fixtures\ChildOfGroupsAnnotationDummy; use Symfony\Component\Serializer\Tests\Fixtures\GroupDummyInterface; /** @@ -23,6 +24,8 @@ class GroupDummy extends GroupDummyParent implements GroupDummyInterface private $foo; #[Groups(["b", "c", "name_converter"])] protected $bar; + #[ChildOfGroupsAnnotationDummy] + protected $quux; private $fooBar; private $symfony; @@ -68,4 +71,14 @@ public function getSymfony() { return $this->symfony; } + + public function getQuux() + { + return $this->quux; + } + + public function setQuux($quux): void + { + $this->quux = $quux; + } } diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/ChildOfGroupsAnnotationDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/ChildOfGroupsAnnotationDummy.php new file mode 100644 index 0000000000000..653758dcad7ae --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/ChildOfGroupsAnnotationDummy.php @@ -0,0 +1,18 @@ +addGroup('name_converter'); $expected->addAttributeMetadata($bar); + $quux = new AttributeMetadata('quux'); + $quux->addGroup('d'); + $expected->addAttributeMetadata($quux); + $fooBar = new AttributeMetadata('fooBar'); $fooBar->addGroup('a'); $fooBar->addGroup('b'); diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ObjectDummy.php b/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ObjectDummy.php index e127724572afd..ac610f098607f 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ObjectDummy.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ObjectDummy.php @@ -5,6 +5,9 @@ class ObjectDummy { protected $foo; + /** + * @var array + */ public $bar; private $baz; protected $camelCase; diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 860c16f6036a4..ec92619911695 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -164,6 +164,19 @@ public function testDenormalize() $this->assertTrue($obj->isBaz()); } + public function testDenormalizeEmptyXmlArray() + { + $normalizer = $this->getDenormalizerForObjectToPopulate(); + $obj = $normalizer->denormalize( + ['bar' => ''], + ObjectDummy::class, + 'xml' + ); + + $this->assertIsArray($obj->bar); + $this->assertEmpty($obj->bar); + } + public function testDenormalizeWithObject() { $data = new \stdClass(); diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php index b2a76656d76fe..519d42ff284d9 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php @@ -115,10 +115,11 @@ public function testNormalizeWithParentClass() $group->setBaz('baz'); $group->setFoo('foo'); $group->setBar('bar'); + $group->setQuux('quux'); $group->setKevin('Kevin'); $group->setCoopTilleuls('coop'); $this->assertEquals( - ['foo' => 'foo', 'bar' => 'bar', 'kevin' => 'Kevin', 'coopTilleuls' => 'coop', 'fooBar' => null, 'symfony' => null, 'baz' => 'baz'], + ['foo' => 'foo', 'bar' => 'bar', 'quux' => 'quux', 'kevin' => 'Kevin', 'coopTilleuls' => 'coop', 'fooBar' => null, 'symfony' => null, 'baz' => 'baz'], $this->normalizer->normalize($group, 'any') ); } diff --git a/src/Symfony/Component/String/Inflector/EnglishInflector.php b/src/Symfony/Component/String/Inflector/EnglishInflector.php index 39b8e9a62ad85..9f2fac675c9cc 100644 --- a/src/Symfony/Component/String/Inflector/EnglishInflector.php +++ b/src/Symfony/Component/String/Inflector/EnglishInflector.php @@ -58,6 +58,9 @@ final class EnglishInflector implements InflectorInterface // selfies (selfie) ['seifles', 7, true, true, 'selfie'], + // zombies (zombie) + ['seibmoz', 7, true, true, 'zombie'], + // movies (movie) ['seivom', 6, true, true, 'movie'], diff --git a/src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php b/src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php index 2edeedfbd3928..3b0a57b14ba1f 100644 --- a/src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php +++ b/src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php @@ -152,6 +152,7 @@ public function singularizeProvider() ['trees', 'tree'], ['waltzes', ['waltz', 'waltze']], ['wives', 'wife'], + ['zombies', 'zombie'], // test casing: if the first letter was uppercase, it should remain so ['Men', 'Man'], diff --git a/src/Symfony/Component/Translation/Bridge/Lokalise/LokaliseProvider.php b/src/Symfony/Component/Translation/Bridge/Lokalise/LokaliseProvider.php index 8aca509cf0f3d..06d053fd0233c 100644 --- a/src/Symfony/Component/Translation/Bridge/Lokalise/LokaliseProvider.php +++ b/src/Symfony/Component/Translation/Bridge/Lokalise/LokaliseProvider.php @@ -148,6 +148,7 @@ private function exportFiles(array $locales, array $domains): array 'directory_prefix' => '%LANG_ISO%', 'filter_langs' => array_values($locales), 'filter_filenames' => array_map([$this, 'getLokaliseFilenameFromDomain'], $domains), + 'export_empty_as' => 'skip', ], ]); diff --git a/src/Symfony/Component/Translation/Bridge/Lokalise/Tests/LokaliseProviderTest.php b/src/Symfony/Component/Translation/Bridge/Lokalise/Tests/LokaliseProviderTest.php index 9156043de3d0e..fe4532a4627ab 100644 --- a/src/Symfony/Component/Translation/Bridge/Lokalise/Tests/LokaliseProviderTest.php +++ b/src/Symfony/Component/Translation/Bridge/Lokalise/Tests/LokaliseProviderTest.php @@ -246,6 +246,7 @@ public function testReadForOneLocaleAndOneDomain(string $locale, string $domain, 'directory_prefix' => '%LANG_ISO%', 'filter_langs' => [$locale], 'filter_filenames' => [$domain.'.xliff'], + 'export_empty_as' => 'skip', ]); $this->assertSame('POST', $method); diff --git a/src/Symfony/Component/Translation/Command/TranslationPullCommand.php b/src/Symfony/Component/Translation/Command/TranslationPullCommand.php index 0ec02ca7b26c7..511c7caec6a84 100644 --- a/src/Symfony/Component/Translation/Command/TranslationPullCommand.php +++ b/src/Symfony/Component/Translation/Command/TranslationPullCommand.php @@ -119,6 +119,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $writeOptions = [ 'path' => end($this->transPaths), 'xliff_version' => $xliffVersion, + 'default_locale' => $this->defaultLocale, ]; if (!$domains) { diff --git a/src/Symfony/Component/Translation/Tests/Command/TranslationPullCommandTest.php b/src/Symfony/Component/Translation/Tests/Command/TranslationPullCommandTest.php index 73b562dec76ff..2d647341bfbf1 100644 --- a/src/Symfony/Component/Translation/Tests/Command/TranslationPullCommandTest.php +++ b/src/Symfony/Component/Translation/Tests/Command/TranslationPullCommandTest.php @@ -348,7 +348,83 @@ public function testPullForceIntlIcuMessages() , file_get_contents($filenameFr)); } - private function createCommandTester(ProviderInterface $provider, array $locales = ['en'], array $domains = ['messages']): CommandTester + public function testPullMessagesWithDefaultLocale() + { + $arrayLoader = new ArrayLoader(); + $filenameFr = $this->createFile(['note' => 'NOTE'], 'fr'); + $filenameEn = $this->createFile(['note' => 'NOTE']); + $locales = ['en', 'fr']; + $domains = ['messages']; + + $providerReadTranslatorBag = new TranslatorBag(); + $providerReadTranslatorBag->addCatalogue($arrayLoader->load([ + 'note' => 'NOTE', + 'new.foo' => 'nouveauFoo', + ], 'fr')); + $providerReadTranslatorBag->addCatalogue($arrayLoader->load([ + 'note' => 'NOTE', + 'new.foo' => 'newFoo', + ], 'en')); + + $provider = $this->createMock(ProviderInterface::class); + $provider->expects($this->once()) + ->method('read') + ->with($domains, $locales) + ->willReturn($providerReadTranslatorBag); + + $provider->expects($this->once()) + ->method('__toString') + ->willReturn('null://default'); + + $tester = $this->createCommandTester($provider, $locales, $domains, 'fr'); + $tester->execute(['--locales' => ['en', 'fr'], '--domains' => ['messages']]); + + $this->assertStringContainsString('[OK] New translations from "null" has been written locally (for "en, fr" locale(s), and "messages" domain(s)).', trim($tester->getDisplay())); + $this->assertXmlStringEqualsXmlString(<< + + +
+ +
+ + + new.foo + newFoo + + + note + NOTE + + +
+
+XLIFF + , file_get_contents($filenameEn)); + $this->assertXmlStringEqualsXmlString(<< + + +
+ +
+ + + new.foo + nouveauFoo + + + note + NOTE + + +
+
+XLIFF + , file_get_contents($filenameFr)); + } + + private function createCommandTester(ProviderInterface $provider, array $locales = ['en'], array $domains = ['messages'], $defaultLocale = 'en'): CommandTester { $writer = new TranslationWriter(); $writer->addDumper('xlf', new XliffFileDumper()); @@ -360,7 +436,7 @@ private function createCommandTester(ProviderInterface $provider, array $locales $this->getProviderCollection($provider, $locales, $domains), $writer, $reader, - 'en', + $defaultLocale, [$this->translationAppDir.'/translations'] ); $application = new Application(); diff --git a/src/Symfony/Component/Uid/UuidV4.php b/src/Symfony/Component/Uid/UuidV4.php index 53428eeb5bf64..897e1ba627213 100644 --- a/src/Symfony/Component/Uid/UuidV4.php +++ b/src/Symfony/Component/Uid/UuidV4.php @@ -24,7 +24,7 @@ public function __construct(string $uuid = null) { if (null === $uuid) { $uuid = random_bytes(16); - $uuid[6] = $uuid[6] & "\x0F" | "\x4F"; + $uuid[6] = $uuid[6] & "\x0F" | "\x40"; $uuid[8] = $uuid[8] & "\x3F" | "\x80"; $uuid = bin2hex($uuid); diff --git a/src/Symfony/Component/Validator/Constraints/UrlValidator.php b/src/Symfony/Component/Validator/Constraints/UrlValidator.php index 196633336a0fd..82e07feb6b792 100644 --- a/src/Symfony/Component/Validator/Constraints/UrlValidator.php +++ b/src/Symfony/Component/Validator/Constraints/UrlValidator.php @@ -25,7 +25,7 @@ class UrlValidator extends ConstraintValidator (%s):// # protocol (((?:[\_\.\pL\pN-]|%%[0-9A-Fa-f]{2})+:)?((?:[\_\.\pL\pN-]|%%[0-9A-Fa-f]{2})+)@)? # basic auth ( - ([\pL\pN\pS]+\.?[\pL\pN\pS\-\_]+)+(\.?([\pL\pN]|xn\-\-[\pL\pN-]+)+\.?) # a domain name + ([\pL\pN\pS\-\_]+\.)*(([\pL\pN]|xn\-\-[\pL\pN-]+)+\.?) # a domain name | # or \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} # an IP address | # or diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.af.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.af.xlf index cbee3bb9d31b8..61b9eac232ca1 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.af.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.af.xlf @@ -358,10 +358,6 @@ This value is not a valid timezone. Hierdie waarde is nie 'n geldige tydsone nie. - - This password has been leaked in a data breach, it must not be used. Please use another password. - This password has been leaked in a data breach, it must not be used. Please use another password. - This value should be between {{ min }} and {{ max }}. Hierdie waarde moet tussen {{ min }} en {{ max }} wees. diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.ar.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.ar.xlf index c6a38c57dab7e..6aa0d594843f6 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.ar.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.ar.xlf @@ -390,6 +390,18 @@ This value should be a valid expression. يجب أن تكون هذه القيمة تعبيرًا صالحًا. + + This value is not a valid CSS color. + هذه القيمة ليست لون CSS صالحًا. + + + This value is not a valid CIDR notation. + هذه القيمة ليست تدوين CIDR صالحًا. + + + The value of the netmask should be between {{ min }} and {{ max }}. + يجب أن تكون قيمة netmask بين {{ min }} و {{ max }}. +
diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.bg.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.bg.xlf index aa136b92e3f06..455ff81679a1b 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.bg.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.bg.xlf @@ -390,6 +390,18 @@ This value should be a valid expression. Стойността трябва да бъде валиден израз. + + This value is not a valid CSS color. + Стойността не е валиден CSS цвят. + + + This value is not a valid CIDR notation. + Стойността не е валидна CIDR нотация. + + + The value of the netmask should be between {{ min }} and {{ max }}. + Стойността на мрежовата маска трябва да бъде между {{ min }} и {{ max }}. +
diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.bs.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.bs.xlf index b17eae9ab85d4..db9c2a51b6da7 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.bs.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.bs.xlf @@ -386,6 +386,14 @@ This value is not a valid International Securities Identification Number (ISIN). Ova vrijednost nije ispravna međunarodna identifikaciona oznaka hartija od vrijednosti (ISIN). + + This value should be a valid expression. + Ova vrijednost bi trebala biti važeći izraz. + + + This value is not a valid CSS color. + Ova vrijednost nije važeća CSS boja. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.cs.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.cs.xlf index 4d990e4d49358..b341436bc5e18 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.cs.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.cs.xlf @@ -390,6 +390,10 @@ This value should be a valid expression. Tato hodnota musí být platný výraz. + + This value is not a valid CSS color. + Tato hodnota není platná barva CSS. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.da.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.da.xlf index 6716585e7c9e3..b76624e79345a 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.da.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.da.xlf @@ -376,7 +376,7 @@ This value should satisfy at least one of the following constraints: - Værdien skal overholde mindst én af følgende krav:: + Værdien skal overholde mindst én af følgende krav: Each element of this collection should satisfy its own set of constraints. @@ -386,6 +386,22 @@ This value is not a valid International Securities Identification Number (ISIN). Værdien er ikke et gyldig International Securities Identification Number (ISIN). + + This value should be a valid expression. + Værdien skal være et gyldigt udtryk. + + + This value is not a valid CSS color. + Værdien skal være en gyldig CSS farve. + + + This value is not a valid CIDR notation. + Værdien er ikke en gyldig CIDR notation. + + + The value of the netmask should be between {{ min }} and {{ max }}. + Værdien af netmasken skal være mellem {{ min }} og {{ max }}. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.de.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.de.xlf index b6ad3f09f3052..00be24fb8ac5f 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.de.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.de.xlf @@ -390,6 +390,18 @@ This value should be a valid expression. Dieser Wert sollte eine gültige Expression sein. + + This value is not a valid CSS color. + Dieser Wert ist keine gültige CSS-Farbe. + + + This value is not a valid CIDR notation. + Dieser Wert entspricht nicht der CIDR-Notation. + + + The value of the netmask should be between {{ min }} and {{ max }}. + Der Wert der Subnetzmaske sollte zwischen {{ min }} und {{ max }} liegen. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.el.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.el.xlf index 3dded07760de0..8ff496bf04600 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.el.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.el.xlf @@ -386,6 +386,14 @@ This value is not a valid International Securities Identification Number (ISIN). Αυτή η τιμή δεν είναι έγκυρο International Securities Identification Number (ISIN). + + This value should be a valid expression. + Αυτή η τιμή πρέπει να είναι έγκυρη έκφραση. + + + This value is not a valid CSS color. + Αυτή η τιμή δεν είναι έγκυρο χρώμα CSS. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf index 84cd9b9dcd9c2..34c54212d842f 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf @@ -390,6 +390,18 @@ This value should be a valid expression. This value should be a valid expression. + + This value is not a valid CSS color. + This value is not a valid CSS color. + + + This value is not a valid CIDR notation. + This value is not a valid CIDR notation. + + + The value of the netmask should be between {{ min }} and {{ max }}. + The value of the netmask should be between {{ min }} and {{ max }}. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf index c73138b0ee277..897d0a45d74fd 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf @@ -390,6 +390,18 @@ This value should be a valid expression. Este valor debería ser una expresión válida. + + This value is not a valid CSS color. + Este valor no es un color CSS válido. + + + This value is not a valid CIDR notation. + Este valor no es una notación CIDR válida. + + + The value of the netmask should be between {{ min }} and {{ max }}. + El valor de la máscara de red debería estar entre {{ min }} y {{ max }}. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.et.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.et.xlf index 930b47f82e95a..3a97b4f294070 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.et.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.et.xlf @@ -390,6 +390,10 @@ This value should be a valid expression. See väärtus pole korrektne avaldis. + + This value is not a valid CSS color. + See väärtus pole korrektne CSS-i värv. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.fa.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.fa.xlf index 12651ea043edf..b72bc6e03e93c 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.fa.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.fa.xlf @@ -4,11 +4,11 @@ This value should be false. - این مقدار باید نادرست(False) باشد. + این مقدار باید نادرست (False) باشد. This value should be true. - این مقدار باید درست(True) باشد. + این مقدار باید درست (True) باشد. This value should be of type {{ type }}. @@ -20,39 +20,39 @@ The value you selected is not a valid choice. - مقدار انتخاب شده شامل گزینه های معتبر نمی باشد. + مقدار انتخاب شده یک گزینه معتبر نمی‌باشد. You must select at least {{ limit }} choice.|You must select at least {{ limit }} choices. - باید حداقل {{ limit }} گزینه انتخاب نمایید.|باید حداقل {{ limit }} گزینه انتخاب نمایید. + شما باید حداقل {{ limit }} گزینه انتخاب نمایید.|شما باید حداقل {{ limit }} گزینه انتخاب نمایید. You must select at most {{ limit }} choice.|You must select at most {{ limit }} choices. - حداکثر {{ limit }} گزینه می توانید انتخاب نمایید.|حداکثر {{ limit }} گزینه می توانید انتخاب نمایید. + شما باید حداکثر {{ limit }} گزینه انتخاب نمایید.|شما باید حداکثر {{ limit }} گزینه انتخاب نمایید. One or more of the given values is invalid. - یک یا چند مقدار نامعتبر وجود دارد. + یک یا چند مقدار داده شده نامعتبر است. The fields {{ fields }} were not expected. - فیلدهای {{ fields }} شامل فیلدهای مورد انتظار نمی باشند. + فیلدهای {{ fields }} مورد انتظار نبود. The fields {{ fields }} are missing. - فیلدهای {{ fields }} کم هستند. + فیلدهای {{ fields }} مفقود شده اند. This value is not a valid date. - این مقدار یک تاریخ معتبر نمی باشد. + این مقدار یک تاریخ معتبر نمی‌باشد. This value is not a valid datetime. - این مقدار یک تاریخ و زمان معتبر نمی باشد. + این مقدار یک تاریخ و زمان معتبر نمی‌باشد. This value is not a valid email address. - این یک رایانامه معتبر نمی باشد. + این یک آدرس رایانامه (ایمیل) معتبر نمی‌باشد. The file could not be found. @@ -60,15 +60,15 @@ The file is not readable. - پرونده قابل خواندن نیست. + فایل قابل خواندن نیست. The file is too large ({{ size }} {{ suffix }}). Allowed maximum size is {{ limit }} {{ suffix }}. - فایل بیش از اندازه بزرگ است({{ size }} {{ suffix }}). حداکثر اندازه مجاز برابر با {{ limit }} {{ suffix }} می باشد. + فایل بیش از اندازه بزرگ است({{ size }} {{ suffix }}). بیشینه (حداکثر) اندازه مجاز برابر با {{ limit }} {{ suffix }} می‌باشد. The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }}. - این نوع فایل مجاز نمی باشد({{ type }}). نوع های مجاز شامل {{ types }} می باشند. + نوع mime این فایل نامعتبر است({{ type }}). انواع mime مجاز {{ types }} هستند. This value should be {{ limit }} or less. @@ -76,7 +76,7 @@ This value is too long. It should have {{ limit }} character or less.|This value is too long. It should have {{ limit }} characters or less. - بسیار طولانی است.حداکثر تعداد حروف مجاز برابر {{ limit }} می باشد.|بسیار طولانی است.حداکثر تعداد حروف مجاز برابر {{ limit }} می باشد. + این مقدار بسیار طولانی است. باید دارای {{limit}} کاراکتر یا کمتر باشد. | این مقدار بسیار طولانی است. باید دارای {{limit}} کاراکتر یا کمتر باشد. This value should be {{ limit }} or more. @@ -84,7 +84,7 @@ This value is too short. It should have {{ limit }} character or more.|This value is too short. It should have {{ limit }} characters or more. - مقدار وارد شده بسیار کوتاه است.تعداد حروف وارد شده، باید حداقل شامل {{ limit }} کاراکتر باشد.|مقدار وارد شده بسیار کوتاه است.تعداد حروف وارد شده، باید حداقل شامل {{ limit }} کاراکتر باشد. + این مقدار بیش از اندازه کوتاه است. باید {{ limit }} کاراکتر یا بیشتر داشته باشد.|این مقدار بیش از اندازه کوتاه است. باید {{ limit }} کاراکتر یا بیشتر داشته باشد. This value should not be blank. @@ -92,23 +92,23 @@ This value should not be null. - این مقدار باید شامل چیزی باشد. + این مقدار نباید خالی باشد. This value should be null. - این مقدار باید شامل چیزی نباشد. + این مقدار باید خالی باشد. This value is not valid. - این مقدار معتبر نمی باشد. + این مقدار معتبر نمی‌باشد. This value is not a valid time. - این مقدار یک زمان صحیح نمی باشد. + این مقدار یک زمان معتبر نمی‌باشد. This value is not a valid URL. - این مقدار شامل یک URL معتبر نمی باشد. + این مقدار شامل یک URL معتبر نمی‌باشد. The two values should be equal. @@ -116,11 +116,11 @@ The file is too large. Allowed maximum size is {{ limit }} {{ suffix }}. - فایل بیش از اندازه بزرگ است. حداکثر اندازه مجاز برابر با {{ limit }} {{ suffix }} می باشد. + فایل بیش از اندازه بزرگ است. بیشینه (حداکثر) اندازه مجاز {{ limit }} {{ suffix }} است. The file is too large. - فایل بیش از اندازه بزرگ می باشد. + فایل بیش از اندازه بزرگ است. The file could not be uploaded. @@ -132,63 +132,63 @@ This file is not a valid image. - این فایل یک تصویر نمی باشد. + این فایل یک تصویر معتبر نمی‌باشد. This is not a valid IP address. - این مقدار یک IP معتبر نمی باشد. + این آدرس IP معتبر نیست. This value is not a valid language. - این مقدار یک زبان صحیح نمی باشد. + این مقدار یک زبان معتبر نمی‌باشد. This value is not a valid locale. - این مقدار یک محل صحیح نمی باشد. + این مقدار یک محل (locale) معتبر نمی‌باشد. This value is not a valid country. - این مقدار یک کشور صحیح نمی باشد. + این مقدار یک کشور معتبر نمی‌باشد. This value is already used. - این مقدار قبلا مورد استفاده قرار گرفته است. + این مقدار قبلاً استفاده شده است. The size of the image could not be detected. - اندازه تصویر قابل شناسایی نمی باشد. + اندازه تصویر قابل شناسایی نمی‌باشد. The image width is too big ({{ width }}px). Allowed maximum width is {{ max_width }}px. - طول تصویر بسیار بزرگ است({{ width }}px). بیشینه طول مجاز {{ max_width }}px می باشد. + عرض تصویر بسیار بزرگ است({{ width }}px). بیشینه (حداکثر) عرض مجاز {{ max_width }}px می‌باشد. The image width is too small ({{ width }}px). Minimum width expected is {{ min_width }}px. - طول تصویر بسیار کوچک است({{ width }}px). کمینه طول موردنظر {{ min_width }}px می باشد. + عرض تصویر بسیار کوچک است({{ width }}px). کمینه (حداقل) عرض مورد انتظار {{ min_width }}px می‌باشد. The image height is too big ({{ height }}px). Allowed maximum height is {{ max_height }}px. - ارتفاع تصویر بسیار بزرگ است({{ height }}px). بیشینه ارتفاع مجاز {{ max_height }}px می باشد. + ارتفاع تصویر بسیار بزرگ است({{ height }}px). بیشینه (حداکثر) ارتفاع مجاز {{ max_height }}px می‌باشد. The image height is too small ({{ height }}px). Minimum height expected is {{ min_height }}px. - ارتفاع تصویر بسیار کوچک است({{ height }}px). کمینه ارتفاع موردنظر {{ min_height }}px می باشد. + ارتفاع تصویر بسیار کوچک است({{ height }}px). کمینه (حداقل) ارتفاع مورد انتظار {{ min_height }}px می‌باشد. This value should be the user's current password. - این مقدار می بایست کلمه عبور کنونی کاربر باشد. + این مقدار باید رمزعبور فعلی کاربر باشد. This value should have exactly {{ limit }} character.|This value should have exactly {{ limit }} characters. - این مقدار می بایست دقیقا {{ limit }} کاراکتر داشته باشد.| این مقدار می بایست دقیقا {{ limit }} کاراکتر داشته باشد. + این مقدار باید دقیقا {{ limit }} کاراکتر داشته باشد.| این مقدار باید دقیقا {{ limit }} کاراکتر داشته باشد. The file was only partially uploaded. - پرونده به صورت جزیی بارگذاری گردیده است. + فایل به صورت جزئی بارگذاری گردیده است. No file was uploaded. - هیچ پرونده ای بارگذاری نگردیده است. + هیچ فایلی بارگذاری نشد. No temporary folder was configured in php.ini. @@ -196,55 +196,55 @@ Cannot write temporary file to disk. - فایل موقتی را نمی توان در دیسک نوشت. + فایل موقتی را نمی‌توان در دیسک نوشت. A PHP extension caused the upload to fail. - یک اکستنشن PHP موجب شد که بارگذاری فایل با شکست مواجه گردد. + یک افزونه PHP باعث شد بارگذاری ناموفق باشد. This collection should contain {{ limit }} element or more.|This collection should contain {{ limit }} elements or more. - این مجموعه می بایست دارای حداقل {{ limit }} عنصر یا بیشتر باشد.|این مجموعه می بایست دارای حداقل {{ limit }} عنصر یا بیشتر باشد. + این مجموعه باید حاوی {{ limit }} عنصر یا بیشتر باشد.|این مجموعه باید حاوی {{ limit }} عنصر یا بیشتر باشد. This collection should contain {{ limit }} element or less.|This collection should contain {{ limit }} elements or less. - این مجموعه می بایست دارای حداکثر {{ limit }} عنصر یا کمتر باشد.|این مجموعه می بایست دارای حداکثر {{ limit }} عنصر یا کمتر باشد. + این مجموعه باید حاوی {{ limit }} عنصر یا کمتر باشد.|این مجموعه باید حاوی {{ limit }} عنصر یا کمتر باشد. This collection should contain exactly {{ limit }} element.|This collection should contain exactly {{ limit }} elements. - این مجموعه می بایست به طور دقیق دارای {{ limit }} عنصر باشد.|این مجموعه می بایست به طور دقیق دارای {{ limit }} عنصر باشد. + این مجموعه باید دقیقا حاوی {{ limit }} عنصر باشد.|این مجموعه باید دقیقا حاوی {{ limit }} عنصر باشد. Invalid card number. - شماره کارت نامعتبر می باشد. + شماره کارت نامعتبر است. Unsupported card type or invalid card number. - نوع کارت پشتیبانی نمی شود و یا شماره کارت نامعتبر می باشد. + نوع کارت پشتیبانی نمی‌شود و یا شماره کارت نامعتبر می‌باشد. This is not a valid International Bank Account Number (IBAN). - این یک شماره حساب بانک بین المللی معتبر نمی باشد(IBAN). + این یک شماره حساب بانک بین المللی معتبر نمی‌باشد(IBAN). This value is not a valid ISBN-10. - این مقدار یک ISBN-10 معتبر نمی باشد. + این مقدار یک ISBN-10 معتبر نمی‌باشد. This value is not a valid ISBN-13. - این مقدار یک ISBN-13 معتبر نمی باشد. + این مقدار یک ISBN-13 معتبر نمی‌باشد. This value is neither a valid ISBN-10 nor a valid ISBN-13. - این مقدار یک ISBN-10 صحیح و یا ISBN-13 معتبر نمی باشد. + این مقدار یک ISBN-10 معتبر و یا ISBN-13 معتبر نمی‌باشد. This value is not a valid ISSN. - این مقدار یک ISSN معتبر نمی باشد. + این مقدار یک ISSN معتبر نمی‌باشد. This value is not a valid currency. - این مقدار یک واحد پول معتبر نمی باشد. + این مقدار یک واحد پول معتبر نمی‌باشد. This value should be equal to {{ compared_value }}. @@ -260,7 +260,7 @@ This value should be identical to {{ compared_value_type }} {{ compared_value }}. - این مقدار باید با {{ compared_value_type }} {{ compared_value }} یکسان باشد. + این مقدار باید برابر {{ compared_value_type }} {{ compared_value }} باشد. This value should be less than {{ compared_value }}. @@ -276,43 +276,43 @@ This value should not be identical to {{ compared_value_type }} {{ compared_value }}. - این مقدار نباید با {{ compared_value_type }} {{ compared_value }} یکسان باشد. + این مقدار نباید برابر {{ compared_value_type }} {{ compared_value }} باشد. The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}. - ابعاد({{ ratio }}) عکس بیش از حد بزرگ است.حداکثر ابعاد مجاز {{ max_ratio }} می باشد. + ابعاد ({{ ratio }}) عکس بیش از حد بزرگ است. بیشینه (حداکثر) ابعاد مجاز {{ max_ratio }} می‌باشد. The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}. - ابعاد({{ ratio }}) عکس بیش از حد کوچک است.حداقل ابعاد مجاز {{ min_ratio }} می باشد. + ابعاد ({{ ratio }}) عکس بیش از حد کوچک است. کمینه (حداقل) ابعاد مورد انتظار {{ min_ratio }} می‌باشد. The image is square ({{ width }}x{{ height }}px). Square images are not allowed. - این تصویر یک مربع({{ width }}x{{ height }}px) می باشد. تصویر مربع مجاز نمی باشد. + این تصویر یک مربع ({{ width }}x{{ height }}px) می‌باشد. تصاویر مربع شکل مجاز نمی‌باشند. The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed. - این تصویر افقی({{ width }}x{{ height }}px) می باشد. تصویر افقی مجاز نمی باشد. + این تصویر افقی ({{ width }}x{{ height }}px) می‌باشد. تصاویر افقی مجاز نمی‌باشند. The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed. - این تصویر عمودی({{ width }}x{{ height }}px) می باشد. تصویر عمودی مجاز نمی باشد. + این تصویر عمودی ({{ width }}x{{ height }}px) می‌باشد. تصاویر عمودی مجاز نمی‌باشند. An empty file is not allowed. - پرونده خالی مجاز نمی باشد. + فایل خالی مجاز نمی‌باشد. The host could not be resolved. - میزبان قابل حل نمی باشد. + میزبان (Host) شناسایی نشد. This value does not match the expected {{ charset }} charset. - این مقدار مطابق با مقدار مورد انتظار {{ charset }} نمی باشد. + این مقدار مطابق charset مورد انتظار {{ charset }} نمی باشد. This is not a valid Business Identifier Code (BIC). - این مقدار یک(BIC) معتبر نمی باشد. + این مقدار یک کد شناسایی کسب‌و‌کار معتبر (BIC) نیست. Error @@ -320,7 +320,7 @@ This is not a valid UUID. - این مقدار یک UUID معتبر نمی باشد. + این مقدار یک UUID معتبر نمی‌باشد. This value should be a multiple of {{ compared_value }}. @@ -328,7 +328,7 @@ This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}. - این(BIC) با IBAN ارتباطی ندارد. + این کد شناسایی کسب‌و‌کار (BIC) با شماره حساب بانکی بین‌المللی (IBAN) {{ iban }} مرتبط نیست. This value should be valid JSON. @@ -336,7 +336,7 @@ This collection should contain only unique elements. - این مجموعه باید تنها شامل عناصر یکتا باشد. + این مجموعه باید فقط حاوی عناصر یکتا باشد. This value should be positive. @@ -390,6 +390,18 @@ This value should be a valid expression. این مقدار باید یک عبارت معتبر باشد. + + This value is not a valid CSS color. + این مقدار یک رنگ معتبر در CSS نیست. + + + This value is not a valid CIDR notation. + این مقدار یک نماد معتبر در CIDR نیست. + + + The value of the netmask should be between {{ min }} and {{ max }}. + مقدار ماسک شبکه (NetMask) باید بین {{ min }} و {{ max }} باشد. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.fi.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.fi.xlf index 423775903d6c4..2e6886b8732d1 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.fi.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.fi.xlf @@ -304,7 +304,7 @@ The host could not be resolved. - The host could not be resolved. + Palvelimeen ei saatu yhteyttä. This value does not match the expected {{ charset }} charset. @@ -386,6 +386,10 @@ This value is not a valid International Securities Identification Number (ISIN). Tämä arvo ei ole kelvollinen ISIN-koodi (International Securities Identification Number). + + This value should be a valid expression. + Tämän arvon on oltava kelvollinen lauseke. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf index c61ff92c6d473..bc03a0a3dc99e 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf @@ -390,6 +390,18 @@ This value should be a valid expression. Cette valeur doit être une expression valide. + + This value is not a valid CSS color. + Cette valeur n'est pas une couleur CSS valide. + + + This value is not a valid CIDR notation. + Cette valeur n'est pas une notation CIDR valide. + + + The value of the netmask should be between {{ min }} and {{ max }}. + La valeur du masque de réseau doit être comprise entre {{ min }} et {{ max }}. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.gl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.gl.xlf index 4bfd7a09c23f5..433236d789066 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.gl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.gl.xlf @@ -386,6 +386,14 @@ This value is not a valid International Securities Identification Number (ISIN). Este valor non é un número de identificación de valores internacionais (ISIN) válido. + + This value should be a valid expression. + Este valor debe ser unha expresión válida. + + + This value is not a valid CSS color. + Este valor non é unha cor CSS válida. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.hr.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.hr.xlf index 58f13b4e149eb..9719bf9bcc5d3 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.hr.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.hr.xlf @@ -390,6 +390,10 @@ This value should be a valid expression. Ova vrijednost mora biti valjani izraz. + + This value is not a valid CSS color. + Ova vrijednost nije važeća CSS boja. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.hu.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.hu.xlf index a3264d5543af4..30b0dbedbbf1d 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.hu.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.hu.xlf @@ -390,6 +390,18 @@ This value should be a valid expression. Ennek az értéknek érvényes kifejezésnek kell lennie. + + This value is not a valid CSS color. + Ez az érték nem egy érvényes CSS szín. + + + This value is not a valid CIDR notation. + Ez az érték nem egy érvényes CIDR jelölés. + + + The value of the netmask should be between {{ min }} and {{ max }}. + Ennek a netmask értéknek {{ min }} és {{ max }} között kell lennie. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.hy.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.hy.xlf index d3d063b303027..f53df123423d7 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.hy.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.hy.xlf @@ -386,6 +386,10 @@ This value is not a valid International Securities Identification Number (ISIN). Այս արժեքը արժեթղթերի նույնականացման միջազգային համարը վավեր չէ(ISIN)։ + + This value should be a valid expression. + Այս արժեքը պետք է լինի վավեր արտահայտություն: + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.id.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.id.xlf index 4793a16f32032..1687f330bc570 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.id.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.id.xlf @@ -388,7 +388,19 @@ This value should be a valid expression. - Nilai ini harus berupa ekspresi yang valid. + Nilai ini harus berupa ekspresi yang sah. + + + This value is not a valid CSS color. + Nilai ini bukan merupakan warna CSS yang sah. + + + This value is not a valid CIDR notation. + Nilai ini bukan merupakan notasi CIDR yang sah. + + + The value of the netmask should be between {{ min }} and {{ max }}. + Nilai dari netmask harus berada diantara {{ min }} dan {{ max }}. diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.it.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.it.xlf index bca112204ddc8..c7cd43784ee63 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.it.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.it.xlf @@ -390,6 +390,18 @@ This value should be a valid expression. Questo valore dovrebbe essere un'espressione valida. + + This value is not a valid CSS color. + Questo valore non è un colore CSS valido. + + + This value is not a valid CIDR notation. + Questo valore non è una notazione CIDR valida. + + + The value of the netmask should be between {{ min }} and {{ max }}. + Il valore della netmask dovrebbe essere compreso tra {{ min }} e {{ max }}. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.lt.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.lt.xlf index eeb0727349573..7a2c4c521b56a 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.lt.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.lt.xlf @@ -390,6 +390,18 @@ This value should be a valid expression. Ši vertė turėtų būti teisinga išraiška. + + This value is not a valid CSS color. + Ši reikšmė nėra tinkama CSS spalva. + + + This value is not a valid CIDR notation. + Ši vertė nėra tinkamas CIDR žymėjimas. + + + The value of the netmask should be between {{ min }} and {{ max }}. + Tinklo kaukės reikšmė turi būti nuo {{ min }} iki {{ max }}. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.lv.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.lv.xlf index d70ffbc722d51..fa85ecdd64877 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.lv.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.lv.xlf @@ -336,23 +336,23 @@ This collection should contain only unique elements. - Šai kolekcijai jāsatur tikai derīgi elementi. + Šai kolekcijai jāsatur tikai unikāli elementi. This value should be positive. - Šai vērtībāi jābūt pozitīvai. + Šai vērtībai jābūt pozitīvai. This value should be either positive or zero. - Šai vērtībāi jābūt pozitīvai vai vienādai ar nulli. + Šai vērtībai jābūt pozitīvai vai vienādai ar nulli. This value should be negative. - Šai vērtībāi jābūt negatīvai. + Šai vērtībai jābūt negatīvai. This value should be either negative or zero. - Šai vērtībāi jābūt negatīvai vai vienādai ar nulli. + Šai vērtībai jābūt negatīvai vai vienādai ar nulli. This value is not a valid timezone. @@ -386,6 +386,22 @@ This value is not a valid International Securities Identification Number (ISIN). Šī vērtība nav derīgs starptautiskais vērtspapīru identifikācijas numurs (ISIN). + + This value should be a valid expression. + Šai vērtībai jābūt korektai izteiksmei. + + + This value is not a valid CSS color. + Šī vērtība nav korekta CSS krāsa. + + + This value is not a valid CIDR notation. + Šī vērtība nav korekts CIDR apzīmējums. + + + The value of the netmask should be between {{ min }} and {{ max }}. + Tīkla maskas (netmask) vērtībai jābūt starp {{ min }} un {{ max }}. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf index c8e75f047424a..97d1da00e9a50 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf @@ -390,6 +390,18 @@ This value should be a valid expression. Deze waarde moet een geldige expressie zijn. + + This value is not a valid CSS color. + Deze waarde is geen geldige CSS kleur. + + + This value is not a valid CIDR notation. + Deze waarde is geen geldige CIDR notatie. + + + The value of the netmask should be between {{ min }} and {{ max }}. + De waarde van de netmask moet zich tussen {{ min }} en {{ max }} bevinden. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.pl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.pl.xlf index 0881f3167293a..b983b2d6c877f 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.pl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.pl.xlf @@ -390,6 +390,18 @@ This value should be a valid expression. Ta wartość powinna być prawidłowym wyrażeniem. + + This value is not a valid CSS color. + Ta wartość nie jest prawidłowym kolorem CSS. + + + This value is not a valid CIDR notation. + Ta wartość nie jest prawidłową notacją CIDR. + + + The value of the netmask should be between {{ min }} and {{ max }}. + Wartość maski podsieci powinna być pomiędzy {{ min }} i {{ max }}. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.pt.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.pt.xlf index 5caa804dd1712..6b1d061b81ed3 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.pt.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.pt.xlf @@ -390,6 +390,10 @@ This value should be a valid expression. Este valor deve ser uma expressão válida. + + This value is not a valid CSS color. + Este valor não é uma cor de CSS válida. + 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 c6297ca90157a..e9fd77a738224 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.pt_BR.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.pt_BR.xlf @@ -390,6 +390,10 @@ This value should be a valid expression. Este valor deve ser uma expressão válida. + + This value is not a valid CSS color. + Este valor não é uma cor CSS válida. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.ro.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.ro.xlf index 64a5c80fb6d24..7fba2cd1e0e73 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.ro.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.ro.xlf @@ -390,6 +390,14 @@ This value should be a valid expression. Această valoare ar trebui să fie o expresie validă. + + This value is not a valid CIDR notation. + Această valoare nu este o notație CIDR validă. + + + The value of the netmask should be between {{ min }} and {{ max }}. + Valoarea netmask-ului trebuie sa fie intre {{ min }} si {{ max }}. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.ru.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.ru.xlf index 2c7a0444ef51e..8705cbb55d0e6 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.ru.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.ru.xlf @@ -390,6 +390,18 @@ This value should be a valid expression. Это значение должно быть корректным выражением. + + This value is not a valid CSS color. + Значение не является корректным CSS цветом. + + + This value is not a valid CIDR notation. + Значение не соответствует нотации CIDR. + + + The value of the netmask should be between {{ min }} and {{ max }}. + Значение маски подсети должно быть от {{ min }} до {{ max }}. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.sk.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.sk.xlf index ad61814197df9..247ccf24021a7 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.sk.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.sk.xlf @@ -390,6 +390,10 @@ This value should be a valid expression. Táto hodnota by mala byť platným výrazom. + + This value is not a valid CSS color. + Táto hodnota nie je platná CSS farba. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.sl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.sl.xlf index 192751650d7fe..2d8f66a2b97a1 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.sl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.sl.xlf @@ -386,6 +386,14 @@ This value is not a valid International Securities Identification Number (ISIN). Ta vrednost ni veljavna mednarodna identifikacijska koda vrednostnih papirjev (ISIN). + + This value should be a valid expression. + Ta vrednost bi morala biti veljaven izraz. + + + This value is not a valid CSS color. + Ta vrednost ni veljavna barva CSS. + 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 fce95a0769ffb..03ef71303039b 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.sr_Cyrl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.sr_Cyrl.xlf @@ -36,7 +36,7 @@ This field was not expected. - Ово поље не очекује. + Ово поље није било очекивано. This field is missing. @@ -100,7 +100,7 @@ This value is not valid. - Вредност је невалидна. + Вредност није валидна. This value is not a valid time. @@ -304,7 +304,7 @@ The host could not be resolved. - Није могуће одредити послужитеља. + Име хоста не може бити разрешено. This value does not match the expected {{ charset }} charset. @@ -368,7 +368,7 @@ This value is not a valid hostname. - Ова вредност није исправно име послужитеља (hostname). + Ова вредност није исправно име хоста. The number of elements in this collection should be a multiple of {{ compared_value }}. @@ -386,6 +386,22 @@ This value is not a valid International Securities Identification Number (ISIN). Ова вредност није исправна међународна идентификациона ознака хартија од вредности (ISIN). + + This value should be a valid expression. + Ова вредност треба да буде валидан израз. + + + This value is not a valid CSS color. + Ова вредност није исправна CSS боја. + + + This value is not a valid CIDR notation. + Ова вредност није исправна CIDR нотација. + + + The value of the netmask should be between {{ min }} and {{ max }}. + Вредност мрежне маске треба бити између {{ min }} и {{ 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 06b164be8a1f8..86453ada2319b 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.sr_Latn.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.sr_Latn.xlf @@ -304,7 +304,7 @@ The host could not be resolved. - Nije moguće odrediti poslužitelja. + Ime hosta ne može biti razrešeno. This value does not match the expected {{ charset }} charset. @@ -386,6 +386,22 @@ This value is not a valid International Securities Identification Number (ISIN). Ova vrednost nije ispravna međunarodna identifikaciona oznaka hartija od vrednosti (ISIN). + + This value should be a valid expression. + Ova vrednost treba da bude validan izraz. + + + This value is not a valid CSS color. + Ova vrednost nije ispravna CSS boja. + + + This value is not a valid CIDR notation. + Ova vrednost nije ispravna CIDR notacija. + + + The value of the netmask should be between {{ min }} and {{ max }}. + Vrednost mrežne maske treba biti između {{ min }} i {{ max }}. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.sv.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.sv.xlf index 4f2ae8c78ea12..c18b84e2c13ca 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.sv.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.sv.xlf @@ -386,6 +386,14 @@ This value is not a valid International Securities Identification Number (ISIN). Det här värdet är inte ett giltigt "International Securities Identification Number" (ISIN). + + This value should be a valid expression. + Det här värdet bör vara ett giltigt uttryck. + + + This value is not a valid CSS color. + Det här värdet är inte en giltig CSS-färg. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.th.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.th.xlf index 847bf3c26888b..8494a02d86f4e 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.th.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.th.xlf @@ -390,6 +390,10 @@ This value should be a valid expression. ค่านี้ควรเป็นนิพจน์ที่ถูกต้อง + + This value is not a valid CSS color. + ค่านี้ไม่ใช่สี CSS ที่ถูกต้อง + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.tl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.tl.xlf index 90fe83bb31cb9..74d5ed5cfca15 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.tl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.tl.xlf @@ -390,6 +390,10 @@ This value should be a valid expression. Ang halagang ito ay dapat wastong ekspresyon. + + This value is not a valid CSS color. + Ang halagang ito ay hindi wastong kulay ng CSS. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.uk.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.uk.xlf index e8845ec005db5..c11f851fb0267 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.uk.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.uk.xlf @@ -390,6 +390,18 @@ This value should be a valid expression. Це значення має бути дійсним виразом. + + This value is not a valid CSS color. + Це значення не є дійсним CSS кольором. + + + This value is not a valid CIDR notation. + Це значення не є дійсною CIDR нотаціею. + + + The value of the netmask should be between {{ min }} and {{ max }}. + Значення в мережевій масці має бути між {{ min }} та {{ max }}. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.uz.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.uz.xlf index e677278c30553..b32fa31b15442 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.uz.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.uz.xlf @@ -390,6 +390,10 @@ This value should be a valid expression. Ushbu qiymat to'g'ri ifoda bo'lishi kerak. + + This value is not a valid CSS color. + Bu qiymat haqiqiy CSS rangi emas. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.vi.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.vi.xlf index 09dd68036f930..00201792253ab 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.vi.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.vi.xlf @@ -390,6 +390,18 @@ This value should be a valid expression. Giá trị này phải là một biểu thức hợp lệ. + + This value is not a valid CSS color. + Giá trị này không phải là màu CSS hợp lệ. + + + This value is not a valid CIDR notation. + Giá trị này không phải là ký hiệu CIDR hợp lệ. + + + The value of the netmask should be between {{ min }} and {{ max }}. + Giá trị của mặt nạ mạng phải nằm trong khoảng từ {{ min }} đến {{ 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 f8d726d1a30ea..a7d49ba98d35c 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.zh_CN.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.zh_CN.xlf @@ -390,6 +390,18 @@ This value should be a valid expression. 该值需为一个有效的表达式。 + + This value is not a valid CSS color. + 该值不是有效的CSS颜色。 + + + This value is not a valid CIDR notation. + 该值不是一个有效的CIDR表示。 + + + The value of the netmask should be between {{ min }} and {{ max }}. + 网络掩码的值应当在 {{ min }} 和 {{ 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 2b58512f3e56b..b1f7fb4a7153f 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.zh_TW.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.zh_TW.xlf @@ -386,6 +386,22 @@ This value is not a valid International Securities Identification Number (ISIN). 該數值不是有效的國際證券識別碼 (ISIN)。 + + This value should be a valid expression. + 該值需為一個有效的表達式。 + + + This value is not a valid CSS color. + 該值不是有效的CSS顏色。 + + + This value is not a valid CIDR notation. + 該值不是一個有效的CIDR表示。 + + + The value of the netmask should be between {{ min }} and {{ max }}. + 網絡掩碼的值應當在 {{ min }} 和 {{ max }} 之間。 + diff --git a/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php index 6d32edf4e3b7c..d88fbff6cd899 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php @@ -101,6 +101,10 @@ public function getValidUrls() ['http://a.pl'], ['http://www.example.com'], ['http://tt.example.com'], + ['http://m.example.com'], + ['http://m.m.m.example.com'], + ['http://example.m.example.com'], + ['https://long-string_with+symbols.m.example.com'], ['http://www.example.com.'], ['http://www.example.museum'], ['https://example.com/'], @@ -263,6 +267,8 @@ public function getInvalidUrls() ['http://'], ['http://www..com'], ['http://www..example.com'], + ['http://www..m.example.com'], + ['http://.m.example.com'], ['http://wwww.example..com'], ['http://.www.example.com'], ]; diff --git a/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php b/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php index baa7a180b0597..7f5cb65eb24c3 100644 --- a/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php @@ -214,18 +214,24 @@ public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, boo if (is_file($f['file']) && 0 <= self::$srcContext) { if (!empty($f['class']) && (is_subclass_of($f['class'], 'Twig\Template') || is_subclass_of($f['class'], 'Twig_Template')) && method_exists($f['class'], 'getDebugInfo')) { - $template = $f['object'] ?? unserialize(sprintf('O:%d:"%s":0:{}', \strlen($f['class']), $f['class'])); - - $ellipsis = 0; - $templateSrc = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : ''); - $templateInfo = $template->getDebugInfo(); - if (isset($templateInfo[$f['line']])) { - if (!method_exists($template, 'getSourceContext') || !is_file($templatePath = $template->getSourceContext()->getPath())) { - $templatePath = null; - } - if ($templateSrc) { - $src = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext, 'twig', $templatePath, $f); - $srcKey = ($templatePath ?: $template->getTemplateName()).':'.$templateInfo[$f['line']]; + $template = null; + if (isset($f['object'])) { + $template = $f['object']; + } elseif ((new \ReflectionClass($f['class']))->isInstantiable()) { + $template = unserialize(sprintf('O:%d:"%s":0:{}', \strlen($f['class']), $f['class'])); + } + if (null !== $template) { + $ellipsis = 0; + $templateSrc = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : ''); + $templateInfo = $template->getDebugInfo(); + if (isset($templateInfo[$f['line']])) { + if (!method_exists($template, 'getSourceContext') || !is_file($templatePath = $template->getSourceContext()->getPath())) { + $templatePath = null; + } + if ($templateSrc) { + $src = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext, 'twig', $templatePath, $f); + $srcKey = ($templatePath ?: $template->getTemplateName()).':'.$templateInfo[$f['line']]; + } } } } diff --git a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php index 1781f469d504e..a57e8b9b6995a 100644 --- a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php @@ -384,6 +384,8 @@ public static function getSignature(array $a) $signature .= 10 > \strlen($v) && !str_contains($v, '\\') ? "'{$v}'" : "'…".\strlen($v)."'"; } elseif (\is_bool($v)) { $signature .= $v ? 'true' : 'false'; + } elseif (\is_object($v)) { + $signature .= 'new '.substr(strrchr('\\'.get_debug_type($v), '\\'), 1); } else { $signature .= $v; } diff --git a/src/Symfony/Component/VarDumper/Caster/XmlReaderCaster.php b/src/Symfony/Component/VarDumper/Caster/XmlReaderCaster.php index fa0b55dc7833e..721513c5d2d7b 100644 --- a/src/Symfony/Component/VarDumper/Caster/XmlReaderCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/XmlReaderCaster.php @@ -44,6 +44,22 @@ class XmlReaderCaster public static function castXmlReader(\XMLReader $reader, array $a, Stub $stub, bool $isNested) { + try { + $properties = [ + 'LOADDTD' => @$reader->getParserProperty(\XMLReader::LOADDTD), + 'DEFAULTATTRS' => @$reader->getParserProperty(\XMLReader::DEFAULTATTRS), + 'VALIDATE' => @$reader->getParserProperty(\XMLReader::VALIDATE), + 'SUBST_ENTITIES' => @$reader->getParserProperty(\XMLReader::SUBST_ENTITIES), + ]; + } catch (\Error $e) { + $properties = [ + 'LOADDTD' => false, + 'DEFAULTATTRS' => false, + 'VALIDATE' => false, + 'SUBST_ENTITIES' => false, + ]; + } + $props = Caster::PREFIX_VIRTUAL.'parserProperties'; $info = [ 'localName' => $reader->localName, @@ -57,12 +73,7 @@ public static function castXmlReader(\XMLReader $reader, array $a, Stub $stub, b 'value' => $reader->value, 'namespaceURI' => $reader->namespaceURI, 'baseURI' => $reader->baseURI ? new LinkStub($reader->baseURI) : $reader->baseURI, - $props => [ - 'LOADDTD' => $reader->getParserProperty(\XMLReader::LOADDTD), - 'DEFAULTATTRS' => $reader->getParserProperty(\XMLReader::DEFAULTATTRS), - 'VALIDATE' => $reader->getParserProperty(\XMLReader::VALIDATE), - 'SUBST_ENTITIES' => $reader->getParserProperty(\XMLReader::SUBST_ENTITIES), - ], + $props => $properties, ]; if ($info[$props] = Caster::filter($info[$props], Caster::EXCLUDE_EMPTY, [], $count)) { diff --git a/src/Symfony/Component/VarDumper/README.md b/src/Symfony/Component/VarDumper/README.md index bdac24477a819..a0da8c9ab3ab5 100644 --- a/src/Symfony/Component/VarDumper/README.md +++ b/src/Symfony/Component/VarDumper/README.md @@ -3,7 +3,7 @@ VarDumper Component The VarDumper component provides mechanisms for walking through any arbitrary PHP variable. It provides a better `dump()` function that you can use instead -of `var_dump`. +of `var_dump()`. Resources --------- diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php index 0a03e230a9675..b9f409d74ce61 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php @@ -15,6 +15,7 @@ use Symfony\Component\VarDumper\Caster\Caster; use Symfony\Component\VarDumper\Caster\ExceptionCaster; use Symfony\Component\VarDumper\Caster\FrameStub; +use Symfony\Component\VarDumper\Caster\TraceStub; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\HtmlDumper; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; @@ -44,15 +45,15 @@ public function testDefaultSettings() #message: "foo" #code: 0 #file: "%sExceptionCasterTest.php" - #line: 28 + #line: %d trace: { - %s%eTests%eCaster%eExceptionCasterTest.php:28 { + %s%eTests%eCaster%eExceptionCasterTest.php:%d { Symfony\Component\VarDumper\Tests\Caster\ExceptionCasterTest->getTestException($msg, &$ref = null) › { › return new \Exception(''.$msg); › } } - %s%eTests%eCaster%eExceptionCasterTest.php:40 { …} + %s%eTests%eCaster%eExceptionCasterTest.php:%d { …} %A EODUMP; @@ -66,13 +67,13 @@ public function testSeek() $expectedDump = <<<'EODUMP' { - %s%eTests%eCaster%eExceptionCasterTest.php:28 { + %s%eTests%eCaster%eExceptionCasterTest.php:%d { Symfony\Component\VarDumper\Tests\Caster\ExceptionCasterTest->getTestException($msg, &$ref = null) › { › return new \Exception(''.$msg); › } } - %s%eTests%eCaster%eExceptionCasterTest.php:65 { …} + %s%eTests%eCaster%eExceptionCasterTest.php:%d { …} %A EODUMP; @@ -89,15 +90,15 @@ public function testNoArgs() #message: "1" #code: 0 #file: "%sExceptionCasterTest.php" - #line: 28 + #line: %d trace: { - %sExceptionCasterTest.php:28 { + %sExceptionCasterTest.php:%d { Symfony\Component\VarDumper\Tests\Caster\ExceptionCasterTest->getTestException($msg, &$ref = null) › { › return new \Exception(''.$msg); › } } - %s%eTests%eCaster%eExceptionCasterTest.php:84 { …} + %s%eTests%eCaster%eExceptionCasterTest.php:%d { …} %A EODUMP; @@ -114,9 +115,9 @@ public function testNoSrcContext() #message: "1" #code: 0 #file: "%sExceptionCasterTest.php" - #line: 28 + #line: %d trace: { - %s%eTests%eCaster%eExceptionCasterTest.php:28 + %s%eTests%eCaster%eExceptionCasterTest.php:%d %s%eTests%eCaster%eExceptionCasterTest.php:%d %A EODUMP; @@ -124,6 +125,30 @@ public function testNoSrcContext() $this->assertDumpMatchesFormat($expectedDump, $e); } + public function testShouldReturnTraceForConcreteTwigWithError() + { + require_once \dirname(__DIR__).'/Fixtures/Twig.php'; + + $innerExc = (new \__TwigTemplate_VarDumperFixture_u75a09(null, __FILE__))->provideError(); + $nestingWrapper = new \stdClass(); + $nestingWrapper->trace = new TraceStub($innerExc->getTrace()); + + $expectedDump = <<<'EODUMP' +{ + +"trace": { + %sTwig.php:%d { + AbstractTwigTemplate->provideError() + › { + › return $this->createError(); + › } + } + %sExceptionCasterTest.php:%d { …} +%A +EODUMP; + + $this->assertDumpMatchesFormat($expectedDump, $nestingWrapper); + } + public function testHtmlDump() { if (ini_get('xdebug.file_link_format') || get_cfg_var('xdebug.file_link_format')) { @@ -146,10 +171,10 @@ public function testHtmlDump() #code: 0 #file: "%s%eVarDumper%eTests%eCaster%eExceptionCasterTest.php" - #line: 28 + #line: %d trace: { %s%eVarDumper%eTests%eCaster%eExceptionCasterTest.php:28 +Stack level %d.">%s%eVarDumper%eTests%eCaster%eExceptionCasterTest.php:%d …%d } } @@ -169,12 +194,12 @@ public function testFrameWithTwig() $f = [ new FrameStub([ 'file' => \dirname(__DIR__).'/Fixtures/Twig.php', - 'line' => 20, + 'line' => 33, 'class' => '__TwigTemplate_VarDumperFixture_u75a09', ]), new FrameStub([ 'file' => \dirname(__DIR__).'/Fixtures/Twig.php', - 'line' => 21, + 'line' => 34, 'class' => '__TwigTemplate_VarDumperFixture_u75a09', 'object' => new \__TwigTemplate_VarDumperFixture_u75a09(null, __FILE__), ]), @@ -186,7 +211,7 @@ public function testFrameWithTwig() class: "__TwigTemplate_VarDumperFixture_u75a09" src: { %sTwig.php:1 { - › + ›%s › foo bar › twig source } @@ -201,12 +226,11 @@ class: "__TwigTemplate_VarDumperFixture_u75a09" %sExceptionCasterTest.php:2 { › foo bar › twig source - › + ›%s } } } ] - EODUMP; $this->assertDumpMatchesFormat($expectedDump, $f); @@ -221,7 +245,7 @@ public function testExcludeVerbosity() #message: "foo" #code: 0 #file: "%sExceptionCasterTest.php" - #line: 28 + #line: %d } EODUMP; diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php index 0423edf1f3cc7..c0c8494507af7 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php @@ -602,6 +602,27 @@ public function testGenerator() $this->assertDumpMatchesFormat($expectedDump, $generator); } + /** + * @requires PHP 8.1 + */ + public function testNewInInitializer() + { + $f = eval('return function ($a = new stdClass()) {};'); + $line = __LINE__ - 1; + + $this->assertDumpMatchesFormat( + <<reader = new \XmlReader(); + + $expectedDump = <<<'EODUMP' +XMLReader { + +nodeType: NONE + …13 +} +EODUMP; + + $this->assertDumpMatchesFormat($expectedDump, $this->reader); + } } diff --git a/src/Symfony/Component/VarDumper/Tests/Fixtures/Twig.php b/src/Symfony/Component/VarDumper/Tests/Fixtures/Twig.php index 8b84d820fcf7c..5d1a73d424b4b 100644 --- a/src/Symfony/Component/VarDumper/Tests/Fixtures/Twig.php +++ b/src/Symfony/Component/VarDumper/Tests/Fixtures/Twig.php @@ -1,7 +1,20 @@ createError(); + } +} + /* foo.twig */ -class __TwigTemplate_VarDumperFixture_u75a09 extends Twig\Template +class __TwigTemplate_VarDumperFixture_u75a09 extends AbstractTwigTemplate { private $path; @@ -28,7 +41,7 @@ public function getTemplateName() public function getDebugInfo() { - return [20 => 1, 21 => 2]; + return [33 => 1, 34 => 2]; } public function getSourceContext()