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() pFad - Phonifier reborn

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

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


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy