diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index ac82d7063d015..426b55def0001 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,20 +1,21 @@ | Q | A | ------------- | --- -| Branch? | master for features / 3.4 up to 4.2 for bug fixes +| Branch? | master for features / 3.4, 4.2 or 4.3 for bug fixes | Bug fix? | yes/no -| New feature? | yes/no +| New feature? | yes/no | BC breaks? | no -| Deprecations? | yes/no +| Deprecations? | yes/no | Tests pass? | yes | Fixed tickets | #... | License | MIT | Doc PR | symfony/symfony-docs#... diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000000000..60990950bf039 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,10 @@ +Security Policy +=============== + +If you found any issues that might have security implications, +please send a report to security[at]symfony.com +DO NOT PUBLISH SECURITY REPORTS PUBLICLY. + +The full [Security Policy][1] is described in the official documentation. + + [1]: https://symfony.com/security diff --git a/.travis.yml b/.travis.yml index 824e29048305f..405eb68e90ac2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,9 +20,6 @@ env: matrix: include: - - php: hhvm-3.18 - sudo: required - group: edge - php: 5.5 env: php_extra="5.6 7.0 7.1" - php: 7.2 @@ -181,12 +178,6 @@ before_install: [[ -e $ext_cache ]] || (tfold ext.symfony_debug "cd src/Symfony/Component/Debug/Resources/ext && phpize && ./configure && make && mv modules/symfony_debug.so $ext_cache && phpize --clean") echo extension = $ext_cache >> $INI elif [[ $PHP = 7.* ]]; then - if ! php --ri sodium > /dev/null; then - # install libsodium - sudo apt-get install libsodium-dev -y - tfold ext.libsodium tpecl libsodium sodium.so $INI - fi - tfold ext.apcu tpecl apcu-5.1.16 apcu.so $INI tfold ext.mongodb tpecl mongodb-1.6.0alpha1 mongodb.so $INI fi diff --git a/CHANGELOG-3.4.md b/CHANGELOG-3.4.md index 5979db7bcdc5f..7b2094530cef7 100644 --- a/CHANGELOG-3.4.md +++ b/CHANGELOG-3.4.md @@ -7,6 +7,26 @@ in 3.4 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v3.4.0...v3.4.1 +* 3.4.28 (2019-05-28) + + * bug #31584 [Workflow] Do not trigger extra guards (lyrixx) + * bug #31349 [WebProfilerBundle] Use absolute URL for profiler links (Alumbrados) + * bug #31541 [DI] fix using bindings with locators of service subscribers (nicolas-grekas) + * bug #31568 [Process] Fix infinite waiting for stopped process (mshavliuk) + * bug #31551 [ProxyManager] isProxyCandidate() does not take into account interfaces (andrerom) + * bug #31335 [Doctrine] Respect parent class contract in ContainerAwareEventManager (Koc) + * bug #31421 [Routing][AnnotationClassLoader] fix utf-8 encoding in default route name (przemyslaw-bogusz) + * bug #31510 Use the current working dir as default first arg in 'link' binary (lyrixx) + * bug #31535 [Debug] Wrap call to require_once in a try/catch (lyrixx) + * bug #31438 [Serializer] Fix denormalization of object with variadic constructor typed argument (ajgarlag) + * bug #31475 [HttpFoundation] Allow set 'None' on samesite cookie flag (markitosgv) + * bug #31261 [Console] Commands with an alias should not be recognized as ambiguous when using register (Simperfit) + * bug #31371 [DI] Removes number of elements information in debug mode (jschaedl) + * bug #31418 [FrameworkBundle] clarify the possible class/interface of the cache (xabbuh) + * bug #31411 [Intl] Fix root fallback locale (ro0NL) + * bug #31377 [Console] Fix auto-complete for ChoiceQuestion (multi-select answers) (battye) + * bug #31380 [WebProfilerBundle] Don't filter submitted IP values (javiereguiluz) + * 3.4.27 (2019-05-01) * bug #31338 Revert "bug #30620 [FrameworkBundle][HttpFoundation] make session service resettable (dmaicher)" (nicolas-grekas) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index ec97d42d4b87b..fddccc428e7c9 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -39,8 +39,8 @@ Symfony is the result of the work of many people who made the code better - Benjamin Eberlei (beberlei) - Igor Wiedler (igorw) - Eriksen Costa (eriksencosta) - - Guilhem Niot (energetick) - Hamza Amrouche (simperfit) + - Guilhem Niot (energetick) - Sarah Khalil (saro0h) - Jonathan Wage (jwage) - Tobias Nyholm (tobias) @@ -66,34 +66,34 @@ Symfony is the result of the work of many people who made the code better - Diego Saint Esteben (dii3g0) - Konstantin Kudryashov (everzet) - Gábor Egyed (1ed) + - Grégoire Paris (greg0ire) - Bilal Amarni (bamarni) - Titouan Galopin (tgalopin) - - Grégoire Paris (greg0ire) - Mathieu Piot (mpiot) - David Maicher (dmaicher) - Florin Patan (florinpatan) - - Gabriel Ostrolucký (gadelat) - Valentin Udaltsov (vudaltsov) + - Konstantin Myakshin (koc) + - Gabriel Ostrolucký (gadelat) - Vladimir Reznichenko (kalessil) - Jáchym Toušek (enumag) - - Konstantin Myakshin (koc) - Michel Weimerskirch (mweimerskirch) - Andrej Hudec (pulzarraider) + - Issei Murasawa (issei_m) - Eric Clemmons (ericclemmons) - Charles Sarrazin (csarrazi) - Christian Raue - - Issei Murasawa (issei_m) - Arnout Boks (aboks) - Deni - Henrik Westphal (snc) - Dariusz Górecki (canni) - Douglas Greenshields (shieldo) + - David Buchmann (dbu) - Dariusz Ruminski - Lee McDermott - Brandon Turner - Luis Cordova (cordoval) - Graham Campbell (graham) - - David Buchmann (dbu) - Daniel Holmes (dholmes) - Toni Uebernickel (havvg) - Bart van den Burg (burgov) @@ -108,9 +108,9 @@ Symfony is the result of the work of many people who made the code better - Maxime STEINHAUSSER - Michal Piotrowski (eventhorizon) - Tim Nagel (merk) + - Chris Wilkinson (thewilkybarkid) - Brice BERNARD (brikou) - Baptiste Clavié (talus) - - Chris Wilkinson (thewilkybarkid) - marc.weistroff - Tomáš Votruba (tomas_votruba) - lenar @@ -152,6 +152,7 @@ Symfony is the result of the work of many people who made the code better - Arnaud Kleinpeter (nanocom) - Jannik Zschiesche (apfelbox) - Guilherme Blanco (guilhermeblanco) + - Jan Schädlich (jschaedl) - SpacePossum - Pablo Godel (pgodel) - Jérémie Augustin (jaugustin) @@ -162,13 +163,13 @@ Symfony is the result of the work of many people who made the code better - Rafael Dohms (rdohms) - jwdeitch - Mikael Pajunen + - François-Xavier de Guillebon (de-gui_f) - Niels Keurentjes (curry684) - Vyacheslav Pavlov - Richard van Laak (rvanlaak) - Richard Shank (iampersistent) - Thomas Rabaix (rande) - Rouven Weßling (realityking) - - François-Xavier de Guillebon (de-gui_f) - Clemens Tolboom - Helmer Aaviksoo - Alessandro Chitolina (alekitto) @@ -181,7 +182,7 @@ Symfony is the result of the work of many people who made the code better - Artur Kotyrba - Tyson Andre - GDIBass - - Jan Schädlich (jschaedl) + - Alexander Schranz (alexander-schranz) - jeremyFreeAgent (Jérémy Romey) (jeremyfreeagent) - James Halsall (jaitsu) - Matthieu Napoli (mnapoli) @@ -201,7 +202,7 @@ Symfony is the result of the work of many people who made the code better - Dennis Benkert (denderello) - DQNEO - Samuel NELA (snela) - - Alexander Schranz (alexander-schranz) + - Vincent Touzet (vincenttouzet) - Gregor Harlan (gharlan) - Gary PEGEOT (gary-p) - Ruben Gonzalez (rubenrua) @@ -226,7 +227,6 @@ Symfony is the result of the work of many people who made the code better - fivestar - Dominique Bongiraud - Jeremy Livingston (jeremylivingston) - - Vincent Touzet (vincenttouzet) - Michael Lee (zerustech) - Matthieu Auger (matthieuauger) - Leszek Prabucki (l3l0) @@ -253,6 +253,7 @@ Symfony is the result of the work of many people who made the code better - Mantis Development - Loïc Faugeron - Hidde Wieringa (hiddewie) + - Andre Rømcke (andrerom) - Marco Pivetta (ocramius) - Rob Frawley 2nd (robfrawley) - julien pauli (jpauli) @@ -268,7 +269,9 @@ Symfony is the result of the work of many people who made the code better - Elnur Abdurrakhimov (elnur) - Manuel Reinhard (sprain) - Danny Berger (dpb587) + - Antonio J. García Lagar (ajgarlag) - Adam Prager (padam87) + - Przemysław Bogusz (przemyslaw-bogusz) - Benoît Burnichon (bburnichon) - Roman Marintšenko (inori) - Xavier Montaña Carreras (xmontana) @@ -295,15 +298,16 @@ Symfony is the result of the work of many people who made the code better - Jan Sorgalla (jsor) - Ray - Chekote + - Antoine Makdessi (amakdessi) - Thomas Adam - Viktor Bocharskyi (bocharsky_bw) - Jhonny Lidfors (jhonne) - Diego Agulló (aeoris) - jdhoek + - David Prévot - Bob den Otter (bopp) - Thomas Schulz (king2500) - Frank de Jonge (frenkynet) - - Andre Rømcke (andrerom) - Nikita Konstantinov - Wodor Wodorski - Thomas Lallement (raziel057) @@ -318,9 +322,7 @@ Symfony is the result of the work of many people who made the code better - Robert Kiss (kepten) - Zan Baldwin (zanderbaldwin) - Roumen Damianoff (roumen) - - Antonio J. García Lagar (ajgarlag) - Kim Hemsø Rasmussen (kimhemsoe) - - Przemysław Bogusz (przemyslaw-bogusz) - Pascal Luna (skalpa) - Wouter Van Hecke - Jérôme Parmentier (lctrs) @@ -334,8 +336,10 @@ Symfony is the result of the work of many people who made the code better - Chad Sikorra (chadsikorra) - Chris Smith (cs278) - Florian Klein (docteurklein) + - Stadly - Manuel Kiessling (manuelkiessling) - Atsuhiro KUBO (iteman) + - Quynh Xuan Nguyen (xuanquynh) - rudy onfroy (ronfroy) - Serkan Yildiz (srknyldz) - Andrew Moore (finewolf) @@ -359,7 +363,6 @@ Symfony is the result of the work of many people who made the code better - Hidde Boomsma (hboomsma) - John Bafford (jbafford) - Raul Fraile (raulfraile) - - David Prévot - Adrian Rudnik (kreischweide) - Francesc Rosàs (frosas) - Romain Pierre (romain-pierre) @@ -412,9 +415,7 @@ Symfony is the result of the work of many people who made the code better - Grzegorz (Greg) Zdanowski (kiler129) - Iker Ibarguren (ikerib) - Kirill chEbba Chebunin (chebba) - - Stadly - Greg Thornton (xdissent) - - Quynh Xuan Nguyen (xuanquynh) - Martin Hujer (martinhujer) - Philipp Cordes - Costin Bereveanu (schniper) @@ -427,6 +428,7 @@ Symfony is the result of the work of many people who made the code better - Pavel Volokitin (pvolok) - Smaine Milianni (ismail1432) - Arthur de Moulins (4rthem) + - Matthias Althaus (althaus) - Nicolas Dewez (nicolas_dewez) - Endre Fejes - Tobias Naumann (tna) @@ -463,6 +465,7 @@ Symfony is the result of the work of many people who made the code better - Steffen Roßkamp - Alexandru Furculita (afurculita) - Valentin Jonovs (valentins-jonovs) + - Laurent VOULLEMIER (lvo) - Jeanmonod David (jeanmonod) - Christopher Davis (chrisguitarguy) - Webnet team (webnet) @@ -568,6 +571,7 @@ Symfony is the result of the work of many people who made the code better - Almog Baku (almogbaku) - Scott Arciszewski - Xavier HAUSHERR + - Christopher Hertel (chertel) - Norbert Orzechowicz (norzechowicz) - Denis Charrier (brucewouaigne) - Matthijs van den Bos (matthijs) @@ -593,7 +597,9 @@ Symfony is the result of the work of many people who made the code better - Martin Morávek (keeo) - Steven Surowiec - Kevin Saliou (kbsali) + - Ruud Kamphuis (ruudk) - Shawn Iwinski + - Gawain Lynch (gawain) - NothingWeAre - Ryan - Alexander Deruwe (aderuwe) @@ -622,7 +628,6 @@ Symfony is the result of the work of many people who made the code better - Sebastian Krebs - Piotr Stankowski - Baptiste Leduc (bleduc) - - Laurent VOULLEMIER (lvo) - Jean-Christophe Cuvelier [Artack] - Simon DELICATA - alcaeus @@ -647,6 +652,7 @@ Symfony is the result of the work of many people who made the code better - Desjardins Jérôme (jewome62) - Kévin THERAGE (kevin_therage) - Simeon Kolev (simeon_kolev9) + - Jonas Elfering - Nahuel Cuesta (ncuesta) - Chris Boden (cboden) - Christophe Villeger (seragan) @@ -704,6 +710,7 @@ Symfony is the result of the work of many people who made the code better - Ulumuddin Yunus (joenoez) - Johann Saunier (prophet777) - Sergey (upyx) + - Andreas Erhard - Michael Devery (mickadoo) - Antoine Corcy - Sascha Grossenbacher @@ -747,6 +754,7 @@ Symfony is the result of the work of many people who made the code better - RJ Garcia - Delf Tonder (leberknecht) - Raulnet + - Ondrej Exner - Mark Sonnabaum - Massimiliano Braglia (massimilianobraglia) - Richard Quadling @@ -756,6 +764,7 @@ Symfony is the result of the work of many people who made the code better - Alexander Volochnev (exelenz) - Michael Piecko - yclian + - Alan Poulain - Aleksey Prilipko - Tomas Norkūnas (norkunas) - Andrew Berry @@ -796,7 +805,6 @@ Symfony is the result of the work of many people who made the code better - Andrew Tchircoff (andrewtch) - michaelwilliams - 1emming - - Matthias Althaus - Leevi Graham (leevigraham) - Nykopol (nykopol) - Tri Pham (phamuyentri) @@ -815,7 +823,6 @@ Symfony is the result of the work of many people who made the code better - fedor.f - Yosmany Garcia (yosmanyga) - Wouter de Wild - - Antoine M (amakdessi) - Degory Valentine - izzyp - Benoit Lévêque (benoit_leveque) @@ -836,7 +843,6 @@ Symfony is the result of the work of many people who made the code better - Ben - Vincent Composieux (eko) - Jayson Xu (superjavason) - - Christopher Hertel (chertel) - Hubert Lenoir (hubert_lenoir) - fago - Harm van Tilborg @@ -888,13 +894,11 @@ Symfony is the result of the work of many people who made the code better - Patrick Allaert - Gustavo Falco (gfalco) - Matt Robinson (inanimatt) - - Ruud Kamphuis (ruudk) - Aleksey Podskrebyshev - Calin Mihai Pristavu - David Marín Carreño (davefx) - Fabien LUCAS (flucas2) - Omar Yepez (oyepez003) - - Gawain Lynch (gawain) - mwsaz - Jelle Kapitein - Benoît Bourgeois @@ -918,6 +922,7 @@ Symfony is the result of the work of many people who made the code better - Morgan Auchede (mauchede) - Sascha Dens (saschadens) - Don Pinkster + - Saif Eddin G - Maksim Muruev - Emil Einarsson - Thomas Landauer @@ -938,6 +943,7 @@ Symfony is the result of the work of many people who made the code better - Arno Geurts - Adán Lobato (adanlobato) - Ian Jenkins (jenkoian) + - Marcos Gómez Vilches (markitosgv) - Matthew Davis (mdavis1982) - Maks - Antoine LA @@ -980,7 +986,9 @@ Symfony is the result of the work of many people who made the code better - d-ph - Renan Taranto (renan-taranto) - Thomas Talbot (ioni) + - Dmitry Simushev - Rikijs Murgs + - Uladzimir Tsykun - Ben Ramsey (ramsey) - Amaury Leroux de Lens (amo__) - Christian Jul Jensen @@ -1005,7 +1013,6 @@ Symfony is the result of the work of many people who made the code better - Sander Marechal - Franz Wilding (killerpoke) - ProgMiner - - Jonas Elfering - Oleg Golovakhin (doc_tr) - Joost van Driel - Icode4Food (icode4food) @@ -1066,6 +1073,7 @@ Symfony is the result of the work of many people who made the code better - Máximo Cuadros (mcuadros) - Lukas Mencl - tamirvs + - gauss - julien.galenski - Christian Neff - Chris Tiearney @@ -1124,6 +1132,7 @@ Symfony is the result of the work of many people who made the code better - hugofonseca (fonsecas72) - Martynas Narbutas - Toon Verwerft (veewee) + - battye - Bailey Parker - Eddie Jaoude - Antanas Arvasevicius @@ -1159,6 +1168,7 @@ Symfony is the result of the work of many people who made the code better - Alexander Cheprasov - Rodrigo Díez Villamuera (rodrigodiez) - James Hudson + - Stephen Clouse - e-ivanov - Einenlum - Jochen Bayer (jocl) @@ -1219,6 +1229,7 @@ Symfony is the result of the work of many people who made the code better - Max Voloshin (maxvoloshin) - Nicolas Fabre (nfabre) - Raul Rodriguez (raul782) + - mshavliuk - WybrenKoelmans - Derek Lambert - MightyBranch @@ -1260,6 +1271,7 @@ Symfony is the result of the work of many people who made the code better - Jelte Steijaert (jelte) - Quique Porta (quiqueporta) - stoccc + - Andrea Quintino (dirk39) - Tomasz Szymczyk (karion) - Alex Vasilchenko - sez-open @@ -1281,7 +1293,6 @@ Symfony is the result of the work of many people who made the code better - Toni Peric (tperic) - Tatsuya Tsuruoka - Ross Tuck - - Andreas Erhard - Kévin Gomez (kevin) - Mihai Nica (redecs) - Soufian EZ-ZANTAR (soezz) @@ -1398,6 +1409,7 @@ Symfony is the result of the work of many people who made the code better - Samuel Gordalina (gordalina) - Max Romanovsky (maxromanovsky) - Nicolas Eeckeloo (neeckeloo) + - Andriy Prokopenko (sleepyboy) - Mathieu Morlon - Daniel Tschinder - Arnaud CHASSEUX @@ -1502,6 +1514,7 @@ Symfony is the result of the work of many people who made the code better - Gavin Staniforth - Alessandro Tagliapietra (alex88) - Biji (biji) + - Jérôme Tanghe (deuchnord) - Alex Teterin (errogaht) - Gunnar Lium (gunnarlium) - Tiago Garcia (tiagojsag) @@ -1512,7 +1525,6 @@ Symfony is the result of the work of many people who made the code better - Enrico Schultz - Evert Harmeling - mschop - - Alan Poulain - Martin Eckhardt - natechicago - Sergei Gorjunov @@ -1645,6 +1657,7 @@ Symfony is the result of the work of many people who made the code better - michalmarcinkowski - Warwick - Chris + - Farid Jalilov - Florent Olivaud - JakeFr - Simon Sargeant @@ -1815,6 +1828,7 @@ Symfony is the result of the work of many people who made the code better - Gabriel Birke - skafandri - Derek Bonner + - martijn - Alan Chen - insidestyles - Maerlyn @@ -1979,6 +1993,7 @@ Symfony is the result of the work of many people who made the code better - patrick-mcdougle - Dariusz Czech - Jack Wright + - MrNicodemuz - Anonymous User - Paweł Tomulik - Eric J. Duran @@ -2004,6 +2019,7 @@ Symfony is the result of the work of many people who made the code better - Tammy D - Daniel STANCU - Ryan Rud + - mmokhi - Ondrej Slinták - vlechemin - Brian Corrigan @@ -2013,6 +2029,7 @@ Symfony is the result of the work of many people who made the code better - fmarchalemisys - mieszko4 - Steve Preston + - Wojciech Skorodecki - Kevin Frantz - Neophy7e - bokonet @@ -2034,6 +2051,7 @@ Symfony is the result of the work of many people who made the code better - Ondřej Führer - Sema - Elan Ruusamäe + - Jon Dufresne - Thorsten Hallwas - Michael Squires - Egor Gorbachev @@ -2079,7 +2097,6 @@ Symfony is the result of the work of many people who made the code better - phc - Дмитрий Пацура - ilyes kooli - - Matthias Althaus - Michaël VEROUX - Julia - Lin Lu @@ -2141,7 +2158,6 @@ Symfony is the result of the work of many people who made the code better - Laurent Bachelier (laurentb) - Luís Cobucci (lcobucci) - Mehdi Achour (machour) - - Marcos Gómez Vilches (markitosgv) - Matthieu Mota (matthieumota) - Matthieu Moquet (mattketmo) - Moritz Borgmann (mborgmann) @@ -2223,6 +2239,7 @@ Symfony is the result of the work of many people who made the code better - Mohamed Karnichi (amiral) - Andrew Carter (andrewcarteruk) - Adam Elsodaney (archfizz) + - Pablo Lozano (arkadis) - Gregório Bonfante Borba (bonfante) - Bogdan Rancichi (devck) - Daniel Kolvik (dkvk) diff --git a/link b/link index 6a2ec15e22102..95a3142d2acda 100755 --- a/link +++ b/link @@ -23,14 +23,12 @@ use Symfony\Component\Filesystem\Filesystem; * @author Kévin Dunglas */ -if (2 !== $argc) { - echo 'Link dependencies to components to a local clone of the main symfony/symfony GitHub repository.'.PHP_EOL.PHP_EOL; - echo "Usage: $argv[0] /path/to/the/project".PHP_EOL; - exit(1); -} +$pathToProject = $argv[1] ?? getcwd(); -if (!is_dir("$argv[1]/vendor/symfony")) { - echo "The directory \"$argv[1]\" does not exist or the dependencies are not installed, did you forget to run \"composer install\" in your project?".PHP_EOL; +if (!is_dir("$pathToProject/vendor/symfony")) { + echo 'Link dependencies to components to a local clone of the main symfony/symfony GitHub repository.'.PHP_EOL.PHP_EOL; + echo "Usage: $argv[0] /path/to/the/project".PHP_EOL.PHP_EOL; + echo "The directory \"$pathToProject\" does not exist or the dependencies are not installed, did you forget to run \"composer install\" in your project?".PHP_EOL; exit(1); } @@ -48,7 +46,7 @@ foreach ($directories as $dir) { } } -foreach (glob("$argv[1]/vendor/symfony/*", GLOB_ONLYDIR | GLOB_NOSORT) as $dir) { +foreach (glob("$pathToProject/vendor/symfony/*", GLOB_ONLYDIR | GLOB_NOSORT) as $dir) { $package = 'symfony/'.basename($dir); if (is_link($dir)) { echo "\"$package\" is already a symlink, skipping.".PHP_EOL; @@ -66,6 +64,6 @@ foreach (glob("$argv[1]/vendor/symfony/*", GLOB_ONLYDIR | GLOB_NOSORT) as $dir) echo "\"$package\" has been linked to \"$sfPackages[$package]\".".PHP_EOL; } -foreach (glob("$argv[1]/var/cache/*") as $cacheDir) { +foreach (glob("$pathToProject/var/cache/*") as $cacheDir) { $filesystem->remove($cacheDir); } diff --git a/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php b/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php index 66b99ecf62065..4496d3ac9a3d9 100644 --- a/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php +++ b/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php @@ -37,51 +37,49 @@ public function __construct(ContainerInterface $container) } /** - * Dispatches an event to all registered listeners. - * - * @param string $eventName The name of the event to dispatch. The name of the event is - * the name of the method that is invoked on listeners. - * @param EventArgs $eventArgs The event arguments to pass to the event handlers/listeners. - * If not supplied, the single empty EventArgs instance is used. - * - * @return bool + * {@inheritdoc} */ public function dispatchEvent($eventName, EventArgs $eventArgs = null) { - if (isset($this->listeners[$eventName])) { - $eventArgs = null === $eventArgs ? EventArgs::getEmptyInstance() : $eventArgs; + if (!isset($this->listeners[$eventName])) { + return; + } - $initialized = isset($this->initialized[$eventName]); + $eventArgs = null === $eventArgs ? EventArgs::getEmptyInstance() : $eventArgs; - foreach ($this->listeners[$eventName] as $hash => $listener) { - if (!$initialized && \is_string($listener)) { - $this->listeners[$eventName][$hash] = $listener = $this->container->get($listener); - } + if (!isset($this->initialized[$eventName])) { + $this->initializeListeners($eventName); + } - $listener->$eventName($eventArgs); - } - $this->initialized[$eventName] = true; + foreach ($this->listeners[$eventName] as $hash => $listener) { + $listener->$eventName($eventArgs); } } /** - * Gets the listeners of a specific event or all listeners. - * - * @param string $event The name of the event - * - * @return array The event listeners for the specified event, or all event listeners + * {@inheritdoc} */ public function getListeners($event = null) { - return $event ? $this->listeners[$event] : $this->listeners; + if (null !== $event) { + if (!isset($this->initialized[$event])) { + $this->initializeListeners($event); + } + + return $this->listeners[$event]; + } + + foreach ($this->listeners as $event => $listeners) { + if (!isset($this->initialized[$event])) { + $this->initializeListeners($event); + } + } + + return $this->listeners; } /** - * Checks whether an event has any registered listeners. - * - * @param string $event - * - * @return bool TRUE if the specified event has any listeners, FALSE otherwise + * {@inheritdoc} */ public function hasListeners($event) { @@ -89,20 +87,11 @@ public function hasListeners($event) } /** - * Adds an event listener that listens on the specified events. - * - * @param string|array $events The event(s) to listen on - * @param object|string $listener The listener object - * - * @throws \RuntimeException + * {@inheritdoc} */ public function addEventListener($events, $listener) { if (\is_string($listener)) { - if ($this->initialized) { - throw new \RuntimeException('Adding lazy-loading listeners after construction is not supported.'); - } - $hash = '_service_'.$listener; } else { // Picks the hash code related to that listener @@ -113,14 +102,15 @@ public function addEventListener($events, $listener) // Overrides listener if a previous one was associated already // Prevents duplicate listeners on same event (same instance only) $this->listeners[$event][$hash] = $listener; + + if (\is_string($listener)) { + unset($this->initialized[$event]); + } } } /** - * Removes an event listener from the specified events. - * - * @param string|array $events - * @param object|string $listener + * {@inheritdoc} */ public function removeEventListener($events, $listener) { @@ -138,4 +128,17 @@ public function removeEventListener($events, $listener) } } } + + /** + * @param string $eventName + */ + private function initializeListeners($eventName) + { + foreach ($this->listeners[$eventName] as $hash => $listener) { + if (\is_string($listener)) { + $this->listeners[$eventName][$hash] = $this->container->get($listener); + } + } + $this->initialized[$eventName] = true; + } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php b/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php index 2e97edb1d2e14..b3fb8bc3ac94e 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php @@ -28,8 +28,8 @@ protected function setUp() public function testDispatchEvent() { - $this->container->set('foobar', $listener1 = new MyListener()); - $this->evm->addEventListener('foo', 'foobar'); + $this->container->set('lazy', $listener1 = new MyListener()); + $this->evm->addEventListener('foo', 'lazy'); $this->evm->addEventListener('foo', $listener2 = new MyListener()); $this->evm->dispatchEvent('foo'); @@ -38,19 +38,69 @@ public function testDispatchEvent() $this->assertTrue($listener2->called); } + public function testAddEventListenerAfterDispatchEvent() + { + $this->container->set('lazy1', $listener1 = new MyListener()); + $this->evm->addEventListener('foo', 'lazy1'); + $this->evm->addEventListener('foo', $listener2 = new MyListener()); + + $this->evm->dispatchEvent('foo'); + + $this->container->set('lazy2', $listener3 = new MyListener()); + $this->evm->addEventListener('foo', 'lazy2'); + $this->evm->addEventListener('foo', $listener4 = new MyListener()); + + $this->evm->dispatchEvent('foo'); + + $this->assertTrue($listener1->called); + $this->assertTrue($listener2->called); + $this->assertTrue($listener3->called); + $this->assertTrue($listener4->called); + } + + public function testGetListenersForEvent() + { + $this->container->set('lazy', $listener1 = new MyListener()); + $this->evm->addEventListener('foo', 'lazy'); + $this->evm->addEventListener('foo', $listener2 = new MyListener()); + + $this->assertSame([$listener1, $listener2], array_values($this->evm->getListeners('foo'))); + } + + public function testGetListeners() + { + $this->container->set('lazy', $listener1 = new MyListener()); + $this->evm->addEventListener('foo', 'lazy'); + $this->evm->addEventListener('foo', $listener2 = new MyListener()); + + $this->assertSame([$listener1, $listener2], array_values($this->evm->getListeners()['foo'])); + } + public function testRemoveEventListener() { - $this->evm->addEventListener('foo', 'bar'); - $this->evm->addEventListener('foo', $listener = new MyListener()); + $this->container->set('lazy', $listener1 = new MyListener()); + $this->evm->addEventListener('foo', 'lazy'); + $this->evm->addEventListener('foo', $listener2 = new MyListener()); + + $this->evm->removeEventListener('foo', $listener2); + $this->assertSame([$listener1], array_values($this->evm->getListeners('foo'))); - $listeners = ['foo' => ['_service_bar' => 'bar', spl_object_hash($listener) => $listener]]; - $this->assertSame($listeners, $this->evm->getListeners()); - $this->assertSame($listeners['foo'], $this->evm->getListeners('foo')); + $this->evm->removeEventListener('foo', 'lazy'); + $this->assertSame([], $this->evm->getListeners('foo')); + } + + public function testRemoveEventListenerAfterDispatchEvent() + { + $this->container->set('lazy', $listener1 = new MyListener()); + $this->evm->addEventListener('foo', 'lazy'); + $this->evm->addEventListener('foo', $listener2 = new MyListener()); + + $this->evm->dispatchEvent('foo'); - $this->evm->removeEventListener('foo', $listener); - $this->assertSame(['_service_bar' => 'bar'], $this->evm->getListeners('foo')); + $this->evm->removeEventListener('foo', $listener2); + $this->assertSame([$listener1], array_values($this->evm->getListeners('foo'))); - $this->evm->removeEventListener('foo', 'bar'); + $this->evm->removeEventListener('foo', 'lazy'); $this->assertSame([], $this->evm->getListeners('foo')); } } diff --git a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php index 4ee40def66b15..7c12b7015e620 100644 --- a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php +++ b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php @@ -47,7 +47,7 @@ public function __construct($salt = '') */ public function isProxyCandidate(Definition $definition) { - return $definition->isLazy() && ($class = $definition->getClass()) && class_exists($class); + return $definition->isLazy() && ($class = $definition->getClass()) && (class_exists($class) || interface_exists($class)); } /** diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php index 2f7ee38cce2a0..f0822d4616422 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php @@ -15,6 +15,7 @@ use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface; /** * Tests for {@see \Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper}. @@ -137,6 +138,7 @@ public function getProxyCandidates() $definitions = [ [new Definition(__CLASS__), true], [new Definition('stdClass'), true], + [new Definition(DumperInterface::class), true], [new Definition(uniqid('foo', true)), false], [new Definition(), false], ]; diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php index 62adea0e55f8c..eb4f8e4f6dc47 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php @@ -137,6 +137,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $options['show_arguments'] = $input->getOption('show-arguments'); $options['raw_text'] = $input->getOption('raw'); $options['output'] = $io; + $options['is_debug'] = $this->getApplication()->getKernel()->isDebug(); $helper->describe($io, $object, $options); if (!$input->getArgument('name') && !$input->getOption('tag') && !$input->getOption('parameter') && $input->isInteractive()) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php index 90c7602604e2e..911e60c08be5b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php @@ -17,6 +17,7 @@ use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; @@ -339,7 +340,11 @@ protected function describeContainerDefinition(Definition $definition, array $op if ($argument instanceof Reference) { $argumentsInformation[] = sprintf('Service(%s)', (string) $argument); } elseif ($argument instanceof IteratorArgument) { - $argumentsInformation[] = sprintf('Iterator (%d element(s))', \count($argument->getValues())); + if ($argument instanceof TaggedIteratorArgument) { + $argumentsInformation[] = sprintf('Tagged Iterator for "%s"%s', $argument->getTag(), $options['is_debug'] ? '' : sprintf(' (%d element(s))', \count($argument->getValues()))); + } else { + $argumentsInformation[] = sprintf('Iterator (%d element(s))', \count($argument->getValues())); + } } elseif ($argument instanceof Definition) { $argumentsInformation[] = 'Inlined Service'; } else { diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml index b1918047f7178..fe93664854826 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml @@ -82,7 +82,7 @@ - + %serializer.mapping.cache.file% diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php index 9bc05e538c76e..248b5396dfdc3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php @@ -182,6 +182,7 @@ abstract protected function getFormat(); private function assertDescription($expectedDescription, $describedObject, array $options = []) { + $options['is_debug'] = false; $options['raw_output'] = true; $options['raw_text'] = true; $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true); diff --git a/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php b/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php index 4333c74d2ff82..d798c03214556 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php @@ -164,7 +164,7 @@ public function toolbarAction(Request $request, $token) $url = null; try { - $url = $this->generator->generate('_profiler', ['token' => $token]); + $url = $this->generator->generate('_profiler', ['token' => $token], UrlGeneratorInterface::ABSOLUTE_URL); } catch (\Exception $e) { // the profiler is not enabled } @@ -299,7 +299,7 @@ public function searchAction(Request $request) $this->profiler->disable(); - $ip = preg_replace('/[^:\d\.]/', '', $request->query->get('ip')); + $ip = $request->query->get('ip'); $method = $request->query->get('method'); $statusCode = $request->query->get('status_code'); $url = $request->query->get('url'); diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_item.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_item.html.twig index 69872418cfb21..d81e877977667 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_item.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_item.html.twig @@ -1,5 +1,5 @@
- {% if link is not defined or link %}{% endif %} + {% if link is not defined or link %}{% endif %}
{{ icon|default('') }}
{% if link|default(false) %}
{% endif %}
{{ text|default('') }}
diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index 6cc5f5c580ecf..4d10c31699731 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -596,6 +596,15 @@ public function find($name) $this->init(); $aliases = []; + + foreach ($this->commands as $command) { + foreach ($command->getAliases() as $alias) { + if (!$this->has($alias)) { + $this->commands[$alias] = $command; + } + } + } + $allCommands = $this->commandLoader ? array_merge($this->commandLoader->getNames(), array_keys($this->commands)) : array_keys($this->commands); $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name); $commands = preg_grep('{^'.$expr.'}', $allCommands); diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php index 677aa764d4a0f..2d910f4356115 100644 --- a/src/Symfony/Component/Console/Helper/QuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php @@ -239,6 +239,7 @@ protected function writeError(OutputInterface $output, \Exception $error) */ private function autocomplete(OutputInterface $output, Question $question, $inputStream, array $autocomplete) { + $fullChoice = ''; $ret = ''; $i = 0; @@ -265,6 +266,7 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu } elseif ("\177" === $c) { // Backspace Character if (0 === $numMatches && 0 !== $i) { --$i; + $fullChoice = substr($fullChoice, 0, -1); // Move cursor backwards $output->write("\033[1D"); } @@ -301,8 +303,10 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu if ($numMatches > 0 && -1 !== $ofs) { $ret = $matches[$ofs]; // Echo out remaining chars for current match - $output->write(substr($ret, $i)); - $i = \strlen($ret); + $remainingCharacters = substr($ret, \strlen(trim($this->mostRecentlyEnteredValue($fullChoice)))); + $output->write($remainingCharacters); + $fullChoice .= $remainingCharacters; + $i = \strlen($fullChoice); } if ("\n" === $c) { @@ -321,14 +325,21 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu $output->write($c); $ret .= $c; + $fullChoice .= $c; ++$i; + $tempRet = $ret; + + if ($question instanceof ChoiceQuestion && $question->isMultiselect()) { + $tempRet = $this->mostRecentlyEnteredValue($fullChoice); + } + $numMatches = 0; $ofs = 0; foreach ($autocomplete as $value) { // If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle) - if (0 === strpos($value, $ret)) { + if (0 === strpos($value, $tempRet)) { $matches[$numMatches++] = $value; } } @@ -340,8 +351,9 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu if ($numMatches > 0 && -1 !== $ofs) { // Save cursor position $output->write("\0337"); - // Write highlighted text - $output->write(''.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $i)).''); + // Write highlighted text, complete the partially entered response + $charactersEntered = \strlen(trim($this->mostRecentlyEnteredValue($fullChoice))); + $output->write(''.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $charactersEntered)).''); // Restore cursor position $output->write("\0338"); } @@ -350,7 +362,22 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu // Reset stty so it behaves normally again shell_exec(sprintf('stty %s', $sttyMode)); - return $ret; + return $fullChoice; + } + + private function mostRecentlyEnteredValue($entered) + { + // Determine the most recent value that the user entered + if (false === strpos($entered, ',')) { + return $entered; + } + + $choices = explode(',', $entered); + if (\strlen($lastChoice = trim($choices[\count($choices) - 1])) > 0) { + return $lastChoice; + } + + return $entered; } /** diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 200568f0701d7..b920d211aaa8f 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -72,8 +72,8 @@ public static function setUpBeforeClass() require_once self::$fixturesPath.'/BarBucCommand.php'; require_once self::$fixturesPath.'/FooSubnamespaced1Command.php'; require_once self::$fixturesPath.'/FooSubnamespaced2Command.php'; - require_once self::$fixturesPath.'/TestTiti.php'; - require_once self::$fixturesPath.'/TestToto.php'; + require_once self::$fixturesPath.'/TestAmbiguousCommandRegistering.php'; + require_once self::$fixturesPath.'/TestAmbiguousCommandRegistering2.php'; } protected function normalizeLineBreaks($text) @@ -164,6 +164,27 @@ public function testRegister() $this->assertEquals('foo', $command->getName(), '->register() registers a new command'); } + public function testRegisterAmbiguous() + { + $code = function (InputInterface $input, OutputInterface $output) { + $output->writeln('It works!'); + }; + + $application = new Application(); + $application + ->register('test-foo') + ->setAliases(['test']) + ->setCode($code); + + $application + ->register('test-bar') + ->setCode($code); + + $tester = new ApplicationTester($application); + $tester->run(['test']); + $this->assertContains('It works!', $tester->getDisplay(true)); + } + public function testAdd() { $application = new Application(); @@ -303,9 +324,9 @@ public function testFindAmbiguousNamespace() public function testFindNonAmbiguous() { $application = new Application(); - $application->add(new \TestTiti()); - $application->add(new \TestToto()); - $this->assertEquals('test-toto', $application->find('test')->getName()); + $application->add(new \TestAmbiguousCommandRegistering()); + $application->add(new \TestAmbiguousCommandRegistering2()); + $this->assertEquals('test-ambiguous', $application->find('test')->getName()); } /** diff --git a/src/Symfony/Component/Console/Tests/Fixtures/TestToto.php b/src/Symfony/Component/Console/Tests/Fixtures/TestAmbiguousCommandRegistering.php similarity index 65% rename from src/Symfony/Component/Console/Tests/Fixtures/TestToto.php rename to src/Symfony/Component/Console/Tests/Fixtures/TestAmbiguousCommandRegistering.php index 2e6a8195938bb..bece09fcdde82 100644 --- a/src/Symfony/Component/Console/Tests/Fixtures/TestToto.php +++ b/src/Symfony/Component/Console/Tests/Fixtures/TestAmbiguousCommandRegistering.php @@ -4,19 +4,19 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -class TestToto extends Command +class TestAmbiguousCommandRegistering extends Command { protected function configure() { $this - ->setName('test-toto') - ->setDescription('The test-toto command') + ->setName('test-ambiguous') + ->setDescription('The test-ambiguous command') ->setAliases(['test']) ; } protected function execute(InputInterface $input, OutputInterface $output) { - $output->write('test-toto'); + $output->write('test-ambiguous'); } } diff --git a/src/Symfony/Component/Console/Tests/Fixtures/TestTiti.php b/src/Symfony/Component/Console/Tests/Fixtures/TestAmbiguousCommandRegistering2.php similarity index 62% rename from src/Symfony/Component/Console/Tests/Fixtures/TestTiti.php rename to src/Symfony/Component/Console/Tests/Fixtures/TestAmbiguousCommandRegistering2.php index 72e29d2a0a2dc..9dde48624546d 100644 --- a/src/Symfony/Component/Console/Tests/Fixtures/TestTiti.php +++ b/src/Symfony/Component/Console/Tests/Fixtures/TestAmbiguousCommandRegistering2.php @@ -4,18 +4,18 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -class TestTiti extends Command +class TestAmbiguousCommandRegistering2 extends Command { protected function configure() { $this - ->setName('test-titi') - ->setDescription('The test:titi command') + ->setName('test-ambiguous2') + ->setDescription('The test-ambiguous2 command') ; } protected function execute(InputInterface $input, OutputInterface $output) { - $output->write('test-titi'); + $output->write('test-ambiguous2'); } } diff --git a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php index 56ba1c6891322..3c030e04983d9 100644 --- a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php @@ -1018,6 +1018,37 @@ public function testTraversableAutocomplete() $this->assertEquals('FooBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); } + public function testTraversableMultiselectAutocomplete() + { + // + // F + // A<3x UP ARROW>,F + // F00o,A,SecurityBundle + // Acme,As<29x BACKSPACE>S + // Ac,As<3x BACKSPACE>d + $inputStream = $this->getInputStream("\nF\t\nA\033[A\033[A\033[A\t,F\t\nF00\177\177o\t,A\033[B\t, SecurityBundle\nAcme\t, As\t\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177S\t\nAc\t,As\t\177\177\177d\t\n"); + + $dialog = new QuestionHelper(); + $helperSet = new HelperSet([new FormatterHelper()]); + $dialog->setHelperSet($helperSet); + + $question = new ChoiceQuestion( + 'Please select a bundle (defaults to AcmeDemoBundle and AsseticBundle)', + ['AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle'], + '0,1' + ); + + // This tests that autocomplete works for all multiselect choices entered by the user + $question->setMultiselect(true); + + $this->assertEquals(['AcmeDemoBundle', 'AsseticBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); + $this->assertEquals(['FooBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); + $this->assertEquals(['AsseticBundle', 'FooBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); + $this->assertEquals(['FooBundle', 'AsseticBundle', 'SecurityBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); + $this->assertEquals(['SecurityBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); + $this->assertEquals(['AcmeDemoBundle', 'AsseticBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); + } + protected function getInputStream($input) { $stream = fopen('php://memory', 'r+', false); diff --git a/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php b/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php index 1ac18ac8f5657..45dcc00d5cb67 100644 --- a/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php +++ b/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php @@ -185,7 +185,11 @@ private function convertFileToClass($path, $file, $prefix) } } - require_once $file; + try { + require_once $file; + } catch (\Throwable $e) { + return null; + } foreach ($candidates as $candidate) { if ($this->classExists($candidate)) { diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php index 93e489c8f6a67..51de4d7ac0978 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php @@ -48,6 +48,12 @@ protected function processValue($value, $isRoot = false) if (!$v instanceof Reference) { throw new InvalidArgumentException(sprintf('Invalid definition for service "%s": an array of references is expected as first argument when the "container.service_locator" tag is set, "%s" found for key "%s".', $this->currentId, \is_object($v) ? \get_class($v) : \gettype($v), $k)); } + + if (\is_int($k)) { + unset($arguments[0][$k]); + + $k = (string) $v; + } $arguments[0][$k] = new ServiceClosureArgument($v); } ksort($arguments[0]); @@ -91,7 +97,11 @@ public static function register(ContainerBuilder $container, array $refMap, $cal ->setPublic(false) ->addTag('container.service_locator'); - if (!$container->has($id = 'service_locator.'.ContainerBuilder::hash($locator))) { + if (null !== $callerId && $container->hasDefinition($callerId)) { + $locator->setBindings($container->getDefinition($callerId)->getBindings()); + } + + if (!$container->hasDefinition($id = 'service_locator.'.ContainerBuilder::hash($locator))) { $container->setDefinition($id, $locator); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php new file mode 100644 index 0000000000000..27ee7db533f6d --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php @@ -0,0 +1,147 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Argument\BoundArgument; +use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ServiceLocator; +use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition; +use Symfony\Component\DependencyInjection\Tests\Fixtures\TestDefinition1; +use Symfony\Component\DependencyInjection\Tests\Fixtures\TestDefinition2; + +require_once __DIR__.'/../Fixtures/includes/classes.php'; + +class ServiceLocatorTagPassTest extends TestCase +{ + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid definition for service "foo": an array of references is expected as first argument when the "container.service_locator" tag is set. + */ + public function testNoServices() + { + $container = new ContainerBuilder(); + + $container->register('foo', ServiceLocator::class) + ->addTag('container.service_locator') + ; + + (new ServiceLocatorTagPass())->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid definition for service "foo": an array of references is expected as first argument when the "container.service_locator" tag is set, "string" found for key "0". + */ + public function testInvalidServices() + { + $container = new ContainerBuilder(); + + $container->register('foo', ServiceLocator::class) + ->setArguments([[ + 'dummy', + ]]) + ->addTag('container.service_locator') + ; + + (new ServiceLocatorTagPass())->process($container); + } + + public function testProcessValue() + { + $container = new ContainerBuilder(); + + $container->register('bar', CustomDefinition::class); + $container->register('baz', CustomDefinition::class); + + $container->register('foo', ServiceLocator::class) + ->setArguments([[ + new Reference('bar'), + new Reference('baz'), + 'some.service' => new Reference('bar'), + ]]) + ->addTag('container.service_locator') + ; + + (new ServiceLocatorTagPass())->process($container); + + /** @var ServiceLocator $locator */ + $locator = $container->get('foo'); + + $this->assertSame(CustomDefinition::class, \get_class($locator('bar'))); + $this->assertSame(CustomDefinition::class, \get_class($locator('baz'))); + $this->assertSame(CustomDefinition::class, \get_class($locator('some.service'))); + } + + public function testServiceWithKeyOverwritesPreviousInheritedKey() + { + $container = new ContainerBuilder(); + + $container->register('bar', TestDefinition1::class); + $container->register('baz', TestDefinition2::class); + + $container->register('foo', ServiceLocator::class) + ->setArguments([[ + new Reference('bar'), + 'bar' => new Reference('baz'), + ]]) + ->addTag('container.service_locator') + ; + + (new ServiceLocatorTagPass())->process($container); + + /** @var ServiceLocator $locator */ + $locator = $container->get('foo'); + + $this->assertSame(TestDefinition2::class, \get_class($locator('bar'))); + } + + public function testInheritedKeyOverwritesPreviousServiceWithKey() + { + $container = new ContainerBuilder(); + + $container->register('bar', TestDefinition1::class); + $container->register('baz', TestDefinition2::class); + + $container->register('foo', ServiceLocator::class) + ->setArguments([[ + 'bar' => new Reference('baz'), + new Reference('bar'), + ]]) + ->addTag('container.service_locator') + ; + + (new ServiceLocatorTagPass())->process($container); + + /** @var ServiceLocator $locator */ + $locator = $container->get('foo'); + + $this->assertSame(TestDefinition1::class, \get_class($locator('bar'))); + } + + public function testBindingsAreCopied() + { + $container = new ContainerBuilder(); + + $container->register('foo') + ->setBindings(['foo' => 'foo']); + + $locator = ServiceLocatorTagPass::register($container, ['foo' => new Reference('foo')], 'foo'); + $locator = $container->getDefinition($locator); + $locator = $container->getDefinition($locator->getFactory()[0]); + + $this->assertSame(['foo'], array_keys($locator->getBindings())); + $this->assertInstanceOf(BoundArgument::class, $locator->getBindings()['foo']); + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 761f7ef4dd08f..a1ccb52b60329 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -235,7 +235,7 @@ public function testAddServiceIdWithUnsupportedCharacters() $class = 'Symfony_DI_PhpDumper_Test_Unsupported_Characters'; $container = new ContainerBuilder(); $container->setParameter("'", 'oh-no'); - $container->register("foo*/oh-no", 'FooClass')->setPublic(true); + $container->register('foo*/oh-no', 'FooClass')->setPublic(true); $container->register('bar$', 'FooClass')->setPublic(true); $container->register('bar$!', 'FooClass')->setPublic(true); $container->compile(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestDefinition2.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestDefinition2.php new file mode 100644 index 0000000000000..2ec8f9cf86879 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestDefinition2.php @@ -0,0 +1,9 @@ +sort = function ($a, $b) { - return strcmp($a->getRealpath() ?: $a->getPathname(), $b->getRealpath() ?: $b->getPathname()); + return strcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname()); }; } elseif (self::SORT_BY_TYPE === $sort) { $this->sort = function ($a, $b) { @@ -49,7 +49,7 @@ public function __construct(\Traversable $iterator, $sort) return 1; } - return strcmp($a->getRealpath() ?: $a->getPathname(), $b->getRealpath() ?: $b->getPathname()); + return strcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname()); }; } elseif (self::SORT_BY_ACCESSED_TIME === $sort) { $this->sort = function ($a, $b) { diff --git a/src/Symfony/Component/Form/FormInterface.php b/src/Symfony/Component/Form/FormInterface.php index 022b60aa1368d..5455e1afc44f2 100644 --- a/src/Symfony/Component/Form/FormInterface.php +++ b/src/Symfony/Component/Form/FormInterface.php @@ -113,11 +113,11 @@ public function getErrors($deep = false, $flatten = true); * @return $this * * @throws Exception\AlreadySubmittedException If the form has already been submitted - * @throws Exception\LogicException If the view data does not match the expected type - * according to {@link FormConfigInterface::getDataClass}. + * @throws Exception\LogicException if the view data does not match the expected type + * according to {@link FormConfigInterface::getDataClass} * @throws Exception\RuntimeException If listeners try to call setData in a cycle or if * the form inherits data from its parent - * @throws Exception\TransformationFailedException If the synchronization failed. + * @throws Exception\TransformationFailedException if the synchronization failed */ public function setData($modelData); diff --git a/src/Symfony/Component/Form/Resources/translations/validators.be.xlf b/src/Symfony/Component/Form/Resources/translations/validators.be.xlf new file mode 100644 index 0000000000000..004ba594b3875 --- /dev/null +++ b/src/Symfony/Component/Form/Resources/translations/validators.be.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Гэта форма не павінна мець дадатковых палей. + + + The uploaded file was too large. Please try to upload a smaller file. + Запампаваны файл быў занадта вялікім. Калі ласка, паспрабуйце запампаваць файл меншага памеру. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF-токен не сапраўдны. Калі ласка, паспрабуйце яшчэ раз адправіць форму. + + + + diff --git a/src/Symfony/Component/Form/Resources/translations/validators.da.xlf b/src/Symfony/Component/Form/Resources/translations/validators.da.xlf index f52f4e0a30db9..346e7cf5746fd 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.da.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.da.xlf @@ -14,6 +14,122 @@ The CSRF token is invalid. Please try to resubmit the form. CSRF-token er ugyldig. + + This value is not a valid currency. + Denne værdi er ikke en gyldig valuta. + + + This value should be equal to {{ compared_value }}. + Denne værdi skal være lig med {{ compared_value }}. + + + This value should be greater than {{ compared_value }}. + Denne værdi skal være større end {{ compared_value }}. + + + This value should be greater than or equal to {{ compared_value }}. + Denne værdi skal være større end eller lig med {{ compared_value }}. + + + This value should be identical to {{ compared_value_type }} {{ compared_value }}. + Denne værdi skal være identisk med {{ compared_value_type }} {{ compared_value }}. + + + This value should be less than {{ compared_value }}. + Denne værdi skal være mindre end {{ compared_value }}. + + + This value should be less than or equal to {{ compared_value }}. + Denne værdi skal være mindre end eller lig med {{ compared_value }}. + + + This value should not be equal to {{ compared_value }}. + Denne værdi bør ikke være lig med {{ compared_value }}. + + + This value should not be identical to {{ compared_value_type }} {{ compared_value }}. + Denne værdi bør ikke være identisk med {{ compared_value_type }} {{ compared_value }}. + + + The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}. + Billedforholdet er for stort ({{ratio}}). Tilladt maksimumsforhold er {{ max_ratio }}. + + + The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}. + Billedforholdet er for lille ({{ ratio }}). Minimumsforventet forventet er {{ min_ratio }}. + + + The image is square ({{ width }}x{{ height }}px). Square images are not allowed. + Billedet er firkantet ({{ width }} x {{ height }} px). Firkantede billeder er ikke tilladt. + + + The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed. + Billedet er landskabsorienteret ({{width}} x {{height}} px). Landskabsorienterede billeder er ikke tilladt + + + The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed. + Billedet er portrætorienteret ({{ width }}x{{ height }}px). Portrætorienterede billeder er ikke tilladt. + + + An empty file is not allowed. + En tom fil er ikke tilladt. + + + The host could not be resolved. + Værten kunne ikke løses. + + + This value does not match the expected {{ charset }} charset. + Denne værdi stemmer ikke overens med den forventede {{ charset }} charset. + + + This is not a valid Business Identifier Code (BIC). + Dette er ikke en gyldig Business Identifier Code (BIC).a + + + This is not a valid UUID. + Dette er ikke en gyldig UUID. + + + This value should be a multiple of {{ compared_value }}. + Denne værdi skal være et flertal af {{ compared_value }}. + + + This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}. + Denne Business Identifier Code (BIC) er ikke forbundet med IBAN {{ iban }}. + + + This value should be valid JSON. + Denne værdi skal være gyldig JSON. + + + This collection should contain only unique elements. + Denne samling bør kun indeholde unikke elementer. + + + This value should be positive. + Denne værdi skal være positiv. + + + This value should be either positive or zero. + Denne værdi skal være enten positiv eller nul. + + + This value should be negative. + Denne værdi skal være negativ. + + + This value should be either negative or zero. + Denne værdi skal være enten negativ eller nul. + + + This value is not a valid timezone. + Denne værdi er ikke en gyldig tidszone. + + + This password has been leaked in a data breach, it must not be used. Please use another password. + Denne adgangskode er blevet lækket i et databrud, det må ikke bruges. Brug venligst en anden adgangskode. + diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php index 8cdbb1959a79e..f918434715e80 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php @@ -17,13 +17,19 @@ class IntegerToLocalizedStringTransformerTest extends TestCase { + private $defaultLocale; + protected function setUp() { - parent::setUp(); - + $this->defaultLocale = \Locale::getDefault(); \Locale::setDefault('en'); } + protected function tearDown() + { + \Locale::setDefault($this->defaultLocale); + } + public function transformWithRoundingProvider() { return [ diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php index 1f05af6128ca7..9baad43549e62 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php @@ -17,13 +17,19 @@ class NumberToLocalizedStringTransformerTest extends TestCase { + private $defaultLocale; + protected function setUp() { - parent::setUp(); - + $this->defaultLocale = \Locale::getDefault(); \Locale::setDefault('en'); } + protected function tearDown() + { + \Locale::setDefault($this->defaultLocale); + } + public function provideTransformations() { return [ diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/PercentToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/PercentToLocalizedStringTransformerTest.php index c6007a398b861..f726edcda466a 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/PercentToLocalizedStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/PercentToLocalizedStringTransformerTest.php @@ -17,13 +17,19 @@ class PercentToLocalizedStringTransformerTest extends TestCase { + private $defaultLocale; + protected function setUp() { - parent::setUp(); - + $this->defaultLocale = \Locale::getDefault(); \Locale::setDefault('en'); } + protected function tearDown() + { + \Locale::setDefault($this->defaultLocale); + } + public function testTransform() { $transformer = new PercentToLocalizedStringTransformer(); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php index 247a5d4b37861..31dfc65af0700 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php @@ -20,17 +20,19 @@ class DateTypeTest extends BaseTypeTest const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\DateType'; private $defaultTimezone; + private $defaultLocale; protected function setUp() { parent::setUp(); $this->defaultTimezone = date_default_timezone_get(); + $this->defaultLocale = \Locale::getDefault(); } protected function tearDown() { date_default_timezone_set($this->defaultTimezone); - \Locale::setDefault('en'); + \Locale::setDefault($this->defaultLocale); } /** diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/MoneyTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/MoneyTypeTest.php index f75ea90249059..34576ec6306ee 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/MoneyTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/MoneyTypeTest.php @@ -17,6 +17,8 @@ class MoneyTypeTest extends BaseTypeTest { const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\MoneyType'; + private $defaultLocale; + protected function setUp() { // we test against different locales, so we need the full @@ -24,6 +26,15 @@ protected function setUp() IntlTestHelper::requireFullIntl($this, false); parent::setUp(); + + $this->defaultLocale = \Locale::getDefault(); + } + + protected function tearDown() + { + parent::tearDown(); + + \Locale::setDefault($this->defaultLocale); } public function testPassMoneyPatternToView() diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php index c19c82b117697..9cc2893c662dc 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php @@ -17,6 +17,8 @@ class NumberTypeTest extends BaseTypeTest { const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\NumberType'; + private $defaultLocale; + protected function setUp() { parent::setUp(); @@ -24,9 +26,17 @@ protected function setUp() // we test against "de_DE", so we need the full implementation IntlTestHelper::requireFullIntl($this, false); + $this->defaultLocale = \Locale::getDefault(); \Locale::setDefault('de_DE'); } + protected function tearDown() + { + parent::tearDown(); + + \Locale::setDefault($this->defaultLocale); + } + public function testDefaultFormatting() { $form = $this->factory->create(static::TESTED_TYPE); diff --git a/src/Symfony/Component/HttpFoundation/Cookie.php b/src/Symfony/Component/HttpFoundation/Cookie.php index e61619aa6dd92..83a97087f1d99 100644 --- a/src/Symfony/Component/HttpFoundation/Cookie.php +++ b/src/Symfony/Component/HttpFoundation/Cookie.php @@ -28,6 +28,7 @@ class Cookie private $raw; private $sameSite; + const SAMESITE_NONE = 'none'; const SAMESITE_LAX = 'lax'; const SAMESITE_STRICT = 'strict'; @@ -128,7 +129,7 @@ public function __construct($name, $value = null, $expire = 0, $path = '/', $dom $sameSite = strtolower($sameSite); } - if (!\in_array($sameSite, [self::SAMESITE_LAX, self::SAMESITE_STRICT, null], true)) { + if (!\in_array($sameSite, [self::SAMESITE_LAX, self::SAMESITE_STRICT, self::SAMESITE_NONE, null], true)) { throw new \InvalidArgumentException('The "sameSite" parameter value is not valid.'); } diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index ae3f51b1724fc..ea3f460c4692b 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -2050,7 +2050,7 @@ private function setPhpDefaultLocale($locale) } } - /* + /** * Returns the prefix as encoded in the string when the string starts with * the given prefix, false otherwise. * diff --git a/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php b/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php index 2bd1d62bdbf12..99e8074214b62 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php +++ b/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php @@ -21,7 +21,7 @@ interface FlashBagInterface extends SessionBagInterface { /** - * Adds a flash message for type. + * Adds a flash message for the given type. * * @param string $type * @param mixed $message @@ -29,12 +29,12 @@ interface FlashBagInterface extends SessionBagInterface public function add($type, $message); /** - * Registers a message for a given type. + * Registers one or more messages for a given type. * * @param string $type - * @param string|array $message + * @param string|array $messages */ - public function set($type, $message); + public function set($type, $messages); /** * Gets flash messages for a given type. diff --git a/src/Symfony/Component/HttpKernel/Controller/ControllerResolverInterface.php b/src/Symfony/Component/HttpKernel/Controller/ControllerResolverInterface.php index afe9fb733748f..95c59036ff3b5 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ControllerResolverInterface.php +++ b/src/Symfony/Component/HttpKernel/Controller/ControllerResolverInterface.php @@ -31,7 +31,7 @@ interface ControllerResolverInterface * As several resolvers can exist for a single application, a resolver must * return false when it is not able to determine the controller. * - * The resolver must only throw an exception when it should be able to load + * The resolver must only throw an exception when it should be able to load a * controller but cannot because of some errors made by the developer. * * @return callable|false A PHP callable representing the Controller, diff --git a/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php b/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php index 1333120e2dd10..997f260d334ee 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php @@ -24,7 +24,7 @@ * All URL paths starting with /_fragment are handled as * content fragments by this listener. * - * If throws an AccessDeniedHttpException exception if the request + * Throws an AccessDeniedHttpException exception if the request * is not signed or if it is not an internal sub-request. * * @author Fabien Potencier diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 2d1aa52352bb8..01a0224cd99d1 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -67,11 +67,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private $requestStackSize = 0; private $resetServices = false; - const VERSION = '3.4.27'; - const VERSION_ID = 30427; + const VERSION = '3.4.28'; + const VERSION_ID = 30428; const MAJOR_VERSION = 3; const MINOR_VERSION = 4; - const RELEASE_VERSION = 27; + const RELEASE_VERSION = 28; const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '11/2020'; diff --git a/src/Symfony/Component/HttpKernel/Tests/Log/LoggerTest.php b/src/Symfony/Component/HttpKernel/Tests/Log/LoggerTest.php index 7354000b16595..17865203f2c96 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Log/LoggerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Log/LoggerTest.php @@ -34,7 +34,7 @@ class LoggerTest extends TestCase protected function setUp() { - $this->tmpFile = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'log'; + $this->tmpFile = tempnam(sys_get_temp_dir(), 'log'); $this->logger = new Logger(LogLevel::DEBUG, $this->tmpFile); } diff --git a/src/Symfony/Component/Intl/CONTRIBUTING.md b/src/Symfony/Component/Intl/CONTRIBUTING.md deleted file mode 100644 index 971e0af7f5903..0000000000000 --- a/src/Symfony/Component/Intl/CONTRIBUTING.md +++ /dev/null @@ -1,91 +0,0 @@ -Contributing to the Intl component -================================== - -A very good way of contributing to the Intl component is by updating the -included data for the ICU version you have installed on your system. - -Preparation ------------ - -To prepare, you need to install the development dependencies of the component. - - $ cd /path/to/Symfony/Component/Intl - $ composer install - -Determining your ICU version ---------------------------- - -The ICU version installed in your PHP environment can be found by running -icu-version.php: - - $ php Resources/bin/icu-version.php - -Updating the ICU data ---------------------- - -To update the data files, run the update-icu-component.php script: - - $ php Resources/bin/update-icu-component.php - -The script needs the binaries "svn" and "make" to be available on your system. -It will download the latest version of the ICU sources for the ICU version -installed in your PHP environment. The script will then compile the "genrb" -binary and use it to compile the ICU data files to binaries. The binaries are -copied to the Resources/ directory of the Icu component found in the -vendor/symfony/icu/ directory. - -Updating the stub data ----------------------- - -In the previous step you updated the Icu component for the ICU version -installed on your system. If you are using the latest ICU version, you should -also create the stub data files which will be used by people who don't have -the intl extension installed. - -To update the stub files, run the update-stubs.php script: - - $ php Resources/bin/update-stubs.php - -The script will fail if you don't have the latest ICU version. If you want to -upgrade the ICU version, adjust the return value of the -`Intl::getIcuStubVersion()` before you run the script. - -The script creates copies of the binary resource bundles in the Icu component -and stores them in the Resources/ directory of the Intl component. The copies -are made for the locale "en" only and are stored in .php files, so that they -can be read even if the intl extension is not available. - -Creating a pull request ------------------------ - -You need to create up to two pull requests: - -* If you updated the Icu component, you need to push that change and create a - pull request in the `symfony/Icu` repository. Make sure to submit the pull - request to the correct master branch. If you updated the ICU data for version - 4.8, your pull request goes to branch `48-master`, for version 49 to - `49-master` and so on. - -* If you updated the stub files of the Intl component, you need to push that - change and create a pull request in the `symfony/symfony` repository. The - pull request should be based on the `master` branch. - -Combining .res files to a .dat-package --------------------------------------- - -The individual *.res files can be combined into a single .dat-file. -Unfortunately, PHP's `ResourceBundle` class is currently not able to handle -.dat-files. - -Once it is, the following steps have to be followed to build the .dat-file: - -1. Package the resource bundles into a single file - - $ find . -name *.res | sed -e "s/\.\///g" > packagelist.txt - $ pkgdata -p region -T build -d . packagelist.txt - -2. Clean up - - $ rm -rf build packagelist.txt - -3. You can now move region.dat to replace the version bundled with Symfony. diff --git a/src/Symfony/Component/Intl/Data/Generator/AbstractDataGenerator.php b/src/Symfony/Component/Intl/Data/Generator/AbstractDataGenerator.php index 885b488392fb8..c05c102eece2f 100644 --- a/src/Symfony/Component/Intl/Data/Generator/AbstractDataGenerator.php +++ b/src/Symfony/Component/Intl/Data/Generator/AbstractDataGenerator.php @@ -12,8 +12,9 @@ namespace Symfony\Component\Intl\Data\Generator; use Symfony\Component\Filesystem\Filesystem; -use Symfony\Component\Intl\Data\Bundle\Compiler\GenrbCompiler; -use Symfony\Component\Intl\Data\Bundle\Reader\BundleReaderInterface; +use Symfony\Component\Intl\Data\Bundle\Compiler\BundleCompilerInterface; +use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReader; +use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface; use Symfony\Component\Intl\Data\Bundle\Reader\IntlBundleReader; use Symfony\Component\Intl\Data\Util\LocaleScanner; @@ -29,7 +30,7 @@ abstract class AbstractDataGenerator private $compiler; private $dirName; - public function __construct(GenrbCompiler $compiler, $dirName) + public function __construct(BundleCompilerInterface $compiler, $dirName) { $this->compiler = $compiler; $this->dirName = (string) $dirName; @@ -39,7 +40,7 @@ public function generateData(GeneratorConfig $config) { $filesystem = new Filesystem(); $localeScanner = new LocaleScanner(); - $reader = new IntlBundleReader(); + $reader = new BundleEntryReader(new IntlBundleReader()); $writers = $config->getBundleWriters(); $tempDir = sys_get_temp_dir().'/icu-data-'.$this->dirName; @@ -98,36 +99,32 @@ public function generateData(GeneratorConfig $config) abstract protected function scanLocales(LocaleScanner $scanner, $sourceDir); /** - * @param GenrbCompiler $compiler - * @param string $sourceDir - * @param string $tempDir + * @param string $sourceDir + * @param string $tempDir */ - abstract protected function compileTemporaryBundles(GenrbCompiler $compiler, $sourceDir, $tempDir); + abstract protected function compileTemporaryBundles(BundleCompilerInterface $compiler, $sourceDir, $tempDir); abstract protected function preGenerate(); /** - * @param BundleReaderInterface $reader - * @param string $tempDir - * @param string $displayLocale + * @param string $tempDir + * @param string $displayLocale * * @return array|null */ - abstract protected function generateDataForLocale(BundleReaderInterface $reader, $tempDir, $displayLocale); + abstract protected function generateDataForLocale(BundleEntryReaderInterface $reader, $tempDir, $displayLocale); /** - * @param BundleReaderInterface $reader - * @param string $tempDir + * @param string $tempDir * * @return array|null */ - abstract protected function generateDataForRoot(BundleReaderInterface $reader, $tempDir); + abstract protected function generateDataForRoot(BundleEntryReaderInterface $reader, $tempDir); /** - * @param BundleReaderInterface $reader - * @param string $tempDir + * @param string $tempDir * * @return array|null */ - abstract protected function generateDataForMeta(BundleReaderInterface $reader, $tempDir); + abstract protected function generateDataForMeta(BundleEntryReaderInterface $reader, $tempDir); } diff --git a/src/Symfony/Component/Intl/Data/Generator/CurrencyDataGenerator.php b/src/Symfony/Component/Intl/Data/Generator/CurrencyDataGenerator.php index 1be618d7878d7..fead9927a9136 100644 --- a/src/Symfony/Component/Intl/Data/Generator/CurrencyDataGenerator.php +++ b/src/Symfony/Component/Intl/Data/Generator/CurrencyDataGenerator.php @@ -11,8 +11,8 @@ namespace Symfony\Component\Intl\Data\Generator; -use Symfony\Component\Intl\Data\Bundle\Compiler\GenrbCompiler; -use Symfony\Component\Intl\Data\Bundle\Reader\BundleReaderInterface; +use Symfony\Component\Intl\Data\Bundle\Compiler\BundleCompilerInterface; +use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface; use Symfony\Component\Intl\Data\Util\ArrayAccessibleResourceBundle; use Symfony\Component\Intl\Data\Util\LocaleScanner; @@ -25,37 +25,20 @@ */ class CurrencyDataGenerator extends AbstractDataGenerator { - const UNKNOWN_CURRENCY_ID = 'XXX'; - const EUROPEAN_COMPOSITE_UNIT_ID = 'XBA'; - const EUROPEAN_MONETARY_UNIT_ID = 'XBB'; - const EUROPEAN_UNIT_OF_ACCOUNT_XBC_ID = 'XBC'; - const EUROPEAN_UNIT_OF_ACCOUNT_XBD_ID = 'XBD'; - const TESTING_CURRENCY_CODE_ID = 'XTS'; - const ADB_UNIT_OF_ACCOUNT_ID = 'XUA'; - const GOLD_ID = 'XAU'; - const SILVER_ID = 'XAG'; - const PLATINUM_ID = 'XPT'; - const PALLADIUM_ID = 'XPD'; - const SUCRE_ID = 'XSU'; - const SPECIAL_DRAWING_RIGHTS_ID = 'XDR'; - - /** - * Monetary units excluded from generation. - */ private static $blacklist = [ - self::UNKNOWN_CURRENCY_ID => true, - self::EUROPEAN_COMPOSITE_UNIT_ID => true, - self::EUROPEAN_MONETARY_UNIT_ID => true, - self::EUROPEAN_UNIT_OF_ACCOUNT_XBC_ID => true, - self::EUROPEAN_UNIT_OF_ACCOUNT_XBD_ID => true, - self::TESTING_CURRENCY_CODE_ID => true, - self::ADB_UNIT_OF_ACCOUNT_ID => true, - self::GOLD_ID => true, - self::SILVER_ID => true, - self::PLATINUM_ID => true, - self::PALLADIUM_ID => true, - self::SUCRE_ID => true, - self::SPECIAL_DRAWING_RIGHTS_ID => true, + 'XBA' => true, // European Composite Unit + 'XBB' => true, // European Monetary Unit + 'XBC' => true, // European Unit of Account (XBC) + 'XBD' => true, // European Unit of Account (XBD) + 'XUA' => true, // ADB Unit of Account + 'XAU' => true, // Gold + 'XAG' => true, // Silver + 'XPT' => true, // Platinum + 'XPD' => true, // Palladium + 'XSU' => true, // Sucre + 'XDR' => true, // Special Drawing Rights + 'XTS' => true, // Testing Currency Code + 'XXX' => true, // Unknown Currency ]; /** @@ -76,7 +59,7 @@ protected function scanLocales(LocaleScanner $scanner, $sourceDir) /** * {@inheritdoc} */ - protected function compileTemporaryBundles(GenrbCompiler $compiler, $sourceDir, $tempDir) + protected function compileTemporaryBundles(BundleCompilerInterface $compiler, $sourceDir, $tempDir) { $compiler->compile($sourceDir.'/curr', $tempDir); $compiler->compile($sourceDir.'/misc/currencyNumericCodes.txt', $tempDir); @@ -93,7 +76,7 @@ protected function preGenerate() /** * {@inheritdoc} */ - protected function generateDataForLocale(BundleReaderInterface $reader, $tempDir, $displayLocale) + protected function generateDataForLocale(BundleEntryReaderInterface $reader, $tempDir, $displayLocale) { $localeBundle = $reader->read($tempDir, $displayLocale); @@ -112,7 +95,7 @@ protected function generateDataForLocale(BundleReaderInterface $reader, $tempDir /** * {@inheritdoc} */ - protected function generateDataForRoot(BundleReaderInterface $reader, $tempDir) + protected function generateDataForRoot(BundleEntryReaderInterface $reader, $tempDir) { $rootBundle = $reader->read($tempDir, 'root'); @@ -125,7 +108,7 @@ protected function generateDataForRoot(BundleReaderInterface $reader, $tempDir) /** * {@inheritdoc} */ - protected function generateDataForMeta(BundleReaderInterface $reader, $tempDir) + protected function generateDataForMeta(BundleEntryReaderInterface $reader, $tempDir) { $rootBundle = $reader->read($tempDir, 'root'); $supplementalDataBundle = $reader->read($tempDir, 'supplementalData'); diff --git a/src/Symfony/Component/Intl/Data/Generator/FallbackTrait.php b/src/Symfony/Component/Intl/Data/Generator/FallbackTrait.php new file mode 100644 index 0000000000000..25e177abfac07 --- /dev/null +++ b/src/Symfony/Component/Intl/Data/Generator/FallbackTrait.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\Intl\Data\Generator; + +use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface; +use Symfony\Component\Intl\Locale; + +/** + * @author Roland Franssen + * + * @internal + */ +trait FallbackTrait +{ + private $fallbackCache = []; + private $generatingFallback = false; + + /** + * @param string $tempDir + * @param string $displayLocale + * + * @return array|null + * + * @see AbstractDataGenerator::generateDataForLocale() + */ + abstract protected function generateDataForLocale(BundleEntryReaderInterface $reader, $tempDir, $displayLocale); + + /** + * @param string $tempDir + * + * @return array|null + * + * @see AbstractDataGenerator::generateDataForRoot() + */ + abstract protected function generateDataForRoot(BundleEntryReaderInterface $reader, $tempDir); + + /** + * @param string $tempDir + * @param string $displayLocale + * + * @return array + */ + private function generateFallbackData(BundleEntryReaderInterface $reader, $tempDir, $displayLocale) + { + if (null === $fallback = Locale::getFallback($displayLocale)) { + return []; + } + + if (isset($this->fallbackCache[$fallback])) { + return $this->fallbackCache[$fallback]; + } + + $prevGeneratingFallback = $this->generatingFallback; + $this->generatingFallback = true; + + try { + $data = 'root' === $fallback ? $this->generateDataForRoot($reader, $tempDir) : $this->generateDataForLocale($reader, $tempDir, $fallback); + } finally { + $this->generatingFallback = $prevGeneratingFallback; + } + + return $this->fallbackCache[$fallback] = $data ?: []; + } +} diff --git a/src/Symfony/Component/Intl/Data/Generator/LanguageDataGenerator.php b/src/Symfony/Component/Intl/Data/Generator/LanguageDataGenerator.php index e4c541b89ad7a..e8695e19319d5 100644 --- a/src/Symfony/Component/Intl/Data/Generator/LanguageDataGenerator.php +++ b/src/Symfony/Component/Intl/Data/Generator/LanguageDataGenerator.php @@ -11,8 +11,8 @@ namespace Symfony\Component\Intl\Data\Generator; -use Symfony\Component\Intl\Data\Bundle\Compiler\GenrbCompiler; -use Symfony\Component\Intl\Data\Bundle\Reader\BundleReaderInterface; +use Symfony\Component\Intl\Data\Bundle\Compiler\BundleCompilerInterface; +use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface; use Symfony\Component\Intl\Data\Util\ArrayAccessibleResourceBundle; use Symfony\Component\Intl\Data\Util\LocaleScanner; use Symfony\Component\Intl\Exception\RuntimeException; @@ -102,7 +102,7 @@ protected function scanLocales(LocaleScanner $scanner, $sourceDir) /** * {@inheritdoc} */ - protected function compileTemporaryBundles(GenrbCompiler $compiler, $sourceDir, $tempDir) + protected function compileTemporaryBundles(BundleCompilerInterface $compiler, $sourceDir, $tempDir) { $compiler->compile($sourceDir.'/lang', $tempDir); $compiler->compile($sourceDir.'/misc/metadata.txt', $tempDir); @@ -119,7 +119,7 @@ protected function preGenerate() /** * {@inheritdoc} */ - protected function generateDataForLocale(BundleReaderInterface $reader, $tempDir, $displayLocale) + protected function generateDataForLocale(BundleEntryReaderInterface $reader, $tempDir, $displayLocale) { $localeBundle = $reader->read($tempDir, $displayLocale); @@ -139,14 +139,14 @@ protected function generateDataForLocale(BundleReaderInterface $reader, $tempDir /** * {@inheritdoc} */ - protected function generateDataForRoot(BundleReaderInterface $reader, $tempDir) + protected function generateDataForRoot(BundleEntryReaderInterface $reader, $tempDir) { } /** * {@inheritdoc} */ - protected function generateDataForMeta(BundleReaderInterface $reader, $tempDir) + protected function generateDataForMeta(BundleEntryReaderInterface $reader, $tempDir) { $rootBundle = $reader->read($tempDir, 'root'); $metadataBundle = $reader->read($tempDir, 'metadata'); diff --git a/src/Symfony/Component/Intl/Data/Generator/LocaleDataGenerator.php b/src/Symfony/Component/Intl/Data/Generator/LocaleDataGenerator.php index 11b1b6b6d82d1..7fb0096e04b81 100644 --- a/src/Symfony/Component/Intl/Data/Generator/LocaleDataGenerator.php +++ b/src/Symfony/Component/Intl/Data/Generator/LocaleDataGenerator.php @@ -12,223 +12,169 @@ namespace Symfony\Component\Intl\Data\Generator; use Symfony\Component\Filesystem\Filesystem; -use Symfony\Component\Intl\Data\Provider\LanguageDataProvider; -use Symfony\Component\Intl\Data\Provider\RegionDataProvider; -use Symfony\Component\Intl\Data\Provider\ScriptDataProvider; +use Symfony\Component\Intl\Data\Bundle\Compiler\BundleCompilerInterface; +use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface; use Symfony\Component\Intl\Data\Util\LocaleScanner; use Symfony\Component\Intl\Exception\MissingResourceException; -use Symfony\Component\Intl\Exception\ResourceBundleNotFoundException; -use Symfony\Component\Intl\Locale; /** * The rule for compiling the locale bundle. * * @author Bernhard Schussek + * @author Roland Franssen * * @internal */ -class LocaleDataGenerator +class LocaleDataGenerator extends AbstractDataGenerator { - private $dirName; - private $languageDataProvider; - private $scriptDataProvider; - private $regionDataProvider; + use FallbackTrait; - public function __construct($dirName, LanguageDataProvider $languageDataProvider, ScriptDataProvider $scriptDataProvider, RegionDataProvider $regionDataProvider) + private $locales = []; + private $localeAliases = []; + + /** + * {@inheritdoc} + */ + protected function scanLocales(LocaleScanner $scanner, $sourceDir) { - $this->dirName = (string) $dirName; - $this->languageDataProvider = $languageDataProvider; - $this->scriptDataProvider = $scriptDataProvider; - $this->regionDataProvider = $regionDataProvider; + $this->locales = $scanner->scanLocales($sourceDir.'/locales'); + $this->localeAliases = $scanner->scanAliases($sourceDir.'/locales'); + + return $this->locales; } - public function generateData(GeneratorConfig $config) + /** + * {@inheritdoc} + */ + protected function compileTemporaryBundles(BundleCompilerInterface $compiler, $sourceDir, $tempDir) { $filesystem = new Filesystem(); - $localeScanner = new LocaleScanner(); + $filesystem->mkdir([ + $tempDir.'/lang', + $tempDir.'/region', + ]); + $compiler->compile($sourceDir.'/lang', $tempDir.'/lang'); + $compiler->compile($sourceDir.'/region', $tempDir.'/region'); + } - $writers = $config->getBundleWriters(); + /** + * {@inheritdoc} + */ + protected function preGenerate() + { + } - // Prepare filesystem directories - foreach ($writers as $targetDir => $writer) { - $filesystem->remove($targetDir.'/'.$this->dirName); - $filesystem->mkdir($targetDir.'/'.$this->dirName); + /** + * {@inheritdoc} + */ + protected function generateDataForLocale(BundleEntryReaderInterface $reader, $tempDir, $displayLocale) + { + // Don't generate aliases, as they are resolved during runtime + // Unless an alias is needed as fallback for de-duplication purposes + if (isset($this->localeAliases[$displayLocale]) && !$this->generatingFallback) { + return; } - $locales = $localeScanner->scanLocales($config->getSourceDir().'/locales'); - $aliases = $localeScanner->scanAliases($config->getSourceDir().'/locales'); - - // Flip to facilitate lookup - $flippedLocales = array_flip($locales); - - // Don't generate names for aliases (names will be generated for the - // locale they are duplicating) - $displayLocales = array_diff_key($flippedLocales, $aliases); - - ksort($displayLocales); - - // Generate a list of (existing) locale fallbacks - $fallbackMapping = $this->generateFallbackMapping($displayLocales, $aliases); - - $localeNames = []; - // Generate locale names for all locales that have translations in // at least the language or the region bundle - foreach ($displayLocales as $displayLocale => $_) { - $localeNames[$displayLocale] = []; - - foreach ($locales as $locale) { - try { - // Generate a locale name in the language of each display locale - // Each locale name has the form: "Language (Script, Region, Variant1, ...) - // Script, Region and Variants are optional. If none of them is - // available, the braces are not printed. - if (null !== ($name = $this->generateLocaleName($locale, $displayLocale))) { - $localeNames[$displayLocale][$locale] = $name; - } - } catch (MissingResourceException $e) { - } catch (ResourceBundleNotFoundException $e) { - } - } - } - - // Process again to de-duplicate locales and their fallback locales - // Only keep the differences - foreach ($displayLocales as $displayLocale => $_) { - $fallback = $displayLocale; - - while (isset($fallbackMapping[$fallback])) { - $fallback = $fallbackMapping[$fallback]; - $localeNames[$displayLocale] = array_diff( - $localeNames[$displayLocale], - $localeNames[$fallback] - ); - } - - // If no names remain to be saved for the current locale, skip it - if (0 === \count($localeNames[$displayLocale])) { + $displayFormat = $reader->readEntry($tempDir.'/lang', $displayLocale, ['localeDisplayPattern']); + $pattern = $displayFormat['pattern'] ?? '{0} ({1})'; + $separator = $displayFormat['separator'] ?? '{0}, {1}'; + $localeNames = []; + foreach ($this->locales as $locale) { + // Ensure a normalized list of pure locales + if (\Locale::getAllVariants($locale)) { continue; } - foreach ($writers as $targetDir => $writer) { - $writer->write($targetDir.'/'.$this->dirName, $displayLocale, [ - 'Names' => $localeNames[$displayLocale], - ]); + try { + // Generate a locale name in the language of each display locale + // Each locale name has the form: "Language (Script, Region, Variant1, ...) + // Script, Region and Variants are optional. If none of them is + // available, the braces are not printed. + $localeNames[$locale] = $this->generateLocaleName($reader, $tempDir, $locale, $displayLocale, $pattern, $separator); + } catch (MissingResourceException $e) { + // Silently ignore incomplete locale names + // In this case one should configure at least one fallback locale that is complete (e.g. English) during + // runtime. Alternatively a translation for the missing resource can be proposed upstream. } } - // Generate aliases, needed to enable proper fallback from alias to its - // target - foreach ($aliases as $alias => $aliasOf) { - foreach ($writers as $targetDir => $writer) { - $writer->write($targetDir.'/'.$this->dirName, $alias, [ - '%%ALIAS' => $aliasOf, - ]); - } - } + $data = [ + 'Names' => $localeNames, + ]; - // Create root file which maps locale codes to locale codes, for fallback - foreach ($writers as $targetDir => $writer) { - $writer->write($targetDir.'/'.$this->dirName, 'meta', [ - 'Locales' => $locales, - 'Aliases' => $aliases, - ]); + // Don't de-duplicate a fallback locale + // Ensures the display locale can be de-duplicated on itself + if ($this->generatingFallback) { + return $data; } - } - - private function generateLocaleName($locale, $displayLocale) - { - $name = null; - $lang = \Locale::getPrimaryLanguage($locale); - $script = \Locale::getScript($locale); - $region = \Locale::getRegion($locale); - $variants = \Locale::getAllVariants($locale); - - // Currently the only available variant is POSIX, which we don't want - // to include in the list - if (\count($variants) > 0) { + // Process again to de-duplicate locale and its fallback locales + // Only keep the differences + $fallbackData = $this->generateFallbackData($reader, $tempDir, $displayLocale); + if (isset($fallbackData['Names'])) { + $data['Names'] = array_diff($data['Names'], $fallbackData['Names']); + } + if (!$data['Names']) { return; } - // Some languages are translated together with their region, - // i.e. "en_GB" is translated as "British English" - // we don't include these languages though because they mess up - // the name sorting - // $name = $this->langBundle->getLanguageName($displayLocale, $lang, $region); + return $data; + } - // Some languages are not translated - // Example: "az" (Azerbaijani) has no translation in "af" (Afrikaans) - if (null === ($name = $this->languageDataProvider->getName($lang, $displayLocale))) { - return; - } + /** + * {@inheritdoc} + */ + protected function generateDataForRoot(BundleEntryReaderInterface $reader, $tempDir) + { + } - // "as" (Assamese) has no "Variants" block - //if (!$langBundle->get('Variants')) { - // continue; - //} + /** + * {@inheritdoc} + */ + protected function generateDataForMeta(BundleEntryReaderInterface $reader, $tempDir) + { + return [ + 'Locales' => $this->locales, + 'Aliases' => $this->localeAliases, + ]; + } + /** + * @return string + */ + private function generateLocaleName(BundleEntryReaderInterface $reader, $tempDir, $locale, $displayLocale, $pattern, $separator) + { + // Apply generic notation using square brackets as described per http://cldr.unicode.org/translation/language-names + $name = str_replace(['(', ')'], ['[', ']'], $reader->readEntry($tempDir.'/lang', $displayLocale, ['Languages', \Locale::getPrimaryLanguage($locale)])); $extras = []; // Discover the name of the script part of the locale // i.e. in zh_Hans_MO, "Hans" is the script - if ($script) { - // Some scripts are not translated into every language - if (null === ($scriptName = $this->scriptDataProvider->getName($script, $displayLocale))) { - return; - } - - $extras[] = $scriptName; + if ($script = \Locale::getScript($locale)) { + $extras[] = str_replace(['(', ')'], ['[', ']'], $reader->readEntry($tempDir.'/lang', $displayLocale, ['Scripts', $script])); } // Discover the name of the region part of the locale // i.e. in de_AT, "AT" is the region - if ($region) { - // Some regions are not translated into every language - if (null === ($regionName = $this->regionDataProvider->getName($region, $displayLocale))) { - return; + if ($region = \Locale::getRegion($locale)) { + if (!RegionDataGenerator::isValidCountryCode($region)) { + throw new MissingResourceException('Skipping "'.$locale.'" due an invalid country.'); } - $extras[] = $regionName; + $extras[] = str_replace(['(', ')'], ['[', ']'], $reader->readEntry($tempDir.'/region', $displayLocale, ['Countries', $region])); } - if (\count($extras) > 0) { - // Remove any existing extras - // For example, in German, zh_Hans is "Chinesisch (vereinfacht)". - // The latter is the script part which is already included in the - // extras and will be appended again with the other extras. - if (preg_match('/^(.+)\s+\([^\)]+\)$/', $name, $matches)) { - $name = $matches[1]; + if ($extras) { + $extra = array_shift($extras); + foreach ($extras as $part) { + $extra = str_replace(['{0}', '{1}'], [$extra, $part], $separator); } - $name .= ' ('.implode(', ', $extras).')'; + $name = str_replace(['{0}', '{1}'], [$name, $extra], $pattern); } return $name; } - - private function generateFallbackMapping(array $displayLocales, array $aliases) - { - $mapping = []; - - foreach ($displayLocales as $displayLocale => $_) { - $mapping[$displayLocale] = null; - $fallback = $displayLocale; - - // Recursively search for a fallback locale until one is found - while (null !== ($fallback = Locale::getFallback($fallback))) { - // Currently, no locale has an alias as fallback locale. - // If this starts to be the case, we need to add code here. - \assert(!isset($aliases[$fallback])); - - // Check whether the fallback exists - if (isset($displayLocales[$fallback])) { - $mapping[$displayLocale] = $fallback; - break; - } - } - } - - return $mapping; - } } diff --git a/src/Symfony/Component/Intl/Data/Generator/RegionDataGenerator.php b/src/Symfony/Component/Intl/Data/Generator/RegionDataGenerator.php index 0728db80309b8..25deae2fa5f90 100644 --- a/src/Symfony/Component/Intl/Data/Generator/RegionDataGenerator.php +++ b/src/Symfony/Component/Intl/Data/Generator/RegionDataGenerator.php @@ -11,8 +11,8 @@ namespace Symfony\Component\Intl\Data\Generator; -use Symfony\Component\Intl\Data\Bundle\Compiler\GenrbCompiler; -use Symfony\Component\Intl\Data\Bundle\Reader\BundleReaderInterface; +use Symfony\Component\Intl\Data\Bundle\Compiler\BundleCompilerInterface; +use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface; use Symfony\Component\Intl\Data\Util\ArrayAccessibleResourceBundle; use Symfony\Component\Intl\Data\Util\LocaleScanner; @@ -27,32 +27,18 @@ */ class RegionDataGenerator extends AbstractDataGenerator { - const UNKNOWN_REGION_ID = 'ZZ'; - const OUTLYING_OCEANIA_REGION_ID = 'QO'; - const EUROPEAN_UNION_ID = 'EU'; - const NETHERLANDS_ANTILLES_ID = 'AN'; - const BOUVET_ISLAND_ID = 'BV'; - const HEARD_MCDONALD_ISLANDS_ID = 'HM'; - const CLIPPERTON_ISLAND_ID = 'CP'; - const EUROZONE_ID = 'EZ'; - const UNITED_NATIONS_ID = 'UN'; - - /** - * Regions excluded from generation. - */ private static $blacklist = [ - self::UNKNOWN_REGION_ID => true, // Look like countries, but are sub-continents - self::OUTLYING_OCEANIA_REGION_ID => true, - self::EUROPEAN_UNION_ID => true, - self::EUROZONE_ID => true, - self::UNITED_NATIONS_ID => true, - // No longer exists - self::NETHERLANDS_ANTILLES_ID => true, + 'QO' => true, // Outlying Oceania + 'EU' => true, // European Union + 'EZ' => true, // Eurozone + 'UN' => true, // United Nations // Uninhabited islands - self::BOUVET_ISLAND_ID => true, - self::HEARD_MCDONALD_ISLANDS_ID => true, - self::CLIPPERTON_ISLAND_ID => true, + 'BV' => true, // Bouvet Island + 'HM' => true, // Heard & McDonald Islands + 'CP' => true, // Clipperton Island + // Misc + 'ZZ' => true, // Unknown Region ]; /** @@ -62,6 +48,20 @@ class RegionDataGenerator extends AbstractDataGenerator */ private $regionCodes = []; + public static function isValidCountryCode($region) + { + if (isset(self::$blacklist[$region])) { + return false; + } + + // WORLD/CONTINENT/SUBCONTINENT/GROUPING + if (ctype_digit($region) || \is_int($region)) { + return false; + } + + return true; + } + /** * {@inheritdoc} */ @@ -73,7 +73,7 @@ protected function scanLocales(LocaleScanner $scanner, $sourceDir) /** * {@inheritdoc} */ - protected function compileTemporaryBundles(GenrbCompiler $compiler, $sourceDir, $tempDir) + protected function compileTemporaryBundles(BundleCompilerInterface $compiler, $sourceDir, $tempDir) { $compiler->compile($sourceDir.'/region', $tempDir); } @@ -89,7 +89,7 @@ protected function preGenerate() /** * {@inheritdoc} */ - protected function generateDataForLocale(BundleReaderInterface $reader, $tempDir, $displayLocale) + protected function generateDataForLocale(BundleEntryReaderInterface $reader, $tempDir, $displayLocale) { $localeBundle = $reader->read($tempDir, $displayLocale); @@ -109,14 +109,14 @@ protected function generateDataForLocale(BundleReaderInterface $reader, $tempDir /** * {@inheritdoc} */ - protected function generateDataForRoot(BundleReaderInterface $reader, $tempDir) + protected function generateDataForRoot(BundleEntryReaderInterface $reader, $tempDir) { } /** * {@inheritdoc} */ - protected function generateDataForMeta(BundleReaderInterface $reader, $tempDir) + protected function generateDataForMeta(BundleEntryReaderInterface $reader, $tempDir) { $rootBundle = $reader->read($tempDir, 'root'); @@ -139,12 +139,7 @@ protected function generateRegionNames(ArrayAccessibleResourceBundle $localeBund $regionNames = []; foreach ($unfilteredRegionNames as $region => $regionName) { - if (isset(self::$blacklist[$region])) { - continue; - } - - // WORLD/CONTINENT/SUBCONTINENT/GROUPING - if (ctype_digit($region) || \is_int($region)) { + if (!self::isValidCountryCode($region)) { continue; } diff --git a/src/Symfony/Component/Intl/Data/Generator/ScriptDataGenerator.php b/src/Symfony/Component/Intl/Data/Generator/ScriptDataGenerator.php index 7ed0625262f68..50f8dd2c10fb0 100644 --- a/src/Symfony/Component/Intl/Data/Generator/ScriptDataGenerator.php +++ b/src/Symfony/Component/Intl/Data/Generator/ScriptDataGenerator.php @@ -11,8 +11,8 @@ namespace Symfony\Component\Intl\Data\Generator; -use Symfony\Component\Intl\Data\Bundle\Compiler\GenrbCompiler; -use Symfony\Component\Intl\Data\Bundle\Reader\BundleReaderInterface; +use Symfony\Component\Intl\Data\Bundle\Compiler\BundleCompilerInterface; +use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface; use Symfony\Component\Intl\Data\Util\LocaleScanner; /** @@ -42,7 +42,7 @@ protected function scanLocales(LocaleScanner $scanner, $sourceDir) /** * {@inheritdoc} */ - protected function compileTemporaryBundles(GenrbCompiler $compiler, $sourceDir, $tempDir) + protected function compileTemporaryBundles(BundleCompilerInterface $compiler, $sourceDir, $tempDir) { $compiler->compile($sourceDir.'/lang', $tempDir); } @@ -58,7 +58,7 @@ protected function preGenerate() /** * {@inheritdoc} */ - protected function generateDataForLocale(BundleReaderInterface $reader, $tempDir, $displayLocale) + protected function generateDataForLocale(BundleEntryReaderInterface $reader, $tempDir, $displayLocale) { $localeBundle = $reader->read($tempDir, $displayLocale); @@ -78,14 +78,14 @@ protected function generateDataForLocale(BundleReaderInterface $reader, $tempDir /** * {@inheritdoc} */ - protected function generateDataForRoot(BundleReaderInterface $reader, $tempDir) + protected function generateDataForRoot(BundleEntryReaderInterface $reader, $tempDir) { } /** * {@inheritdoc} */ - protected function generateDataForMeta(BundleReaderInterface $reader, $tempDir) + protected function generateDataForMeta(BundleEntryReaderInterface $reader, $tempDir) { $rootBundle = $reader->read($tempDir, 'root'); diff --git a/src/Symfony/Component/Intl/Data/Provider/CurrencyDataProvider.php b/src/Symfony/Component/Intl/Data/Provider/CurrencyDataProvider.php index dacde26f83707..f31128535ed5d 100644 --- a/src/Symfony/Component/Intl/Data/Provider/CurrencyDataProvider.php +++ b/src/Symfony/Component/Intl/Data/Provider/CurrencyDataProvider.php @@ -13,7 +13,6 @@ use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface; use Symfony\Component\Intl\Exception\MissingResourceException; -use Symfony\Component\Intl\Locale; /** * Data provider for currency-related data. @@ -53,7 +52,7 @@ public function getCurrencies() public function getSymbol($currency, $displayLocale = null) { if (null === $displayLocale) { - $displayLocale = Locale::getDefault(); + $displayLocale = \Locale::getDefault(); } return $this->reader->readEntry($this->path, $displayLocale, ['Names', $currency, static::INDEX_SYMBOL]); @@ -62,7 +61,7 @@ public function getSymbol($currency, $displayLocale = null) public function getName($currency, $displayLocale = null) { if (null === $displayLocale) { - $displayLocale = Locale::getDefault(); + $displayLocale = \Locale::getDefault(); } return $this->reader->readEntry($this->path, $displayLocale, ['Names', $currency, static::INDEX_NAME]); @@ -71,7 +70,7 @@ public function getName($currency, $displayLocale = null) public function getNames($displayLocale = null) { if (null === $displayLocale) { - $displayLocale = Locale::getDefault(); + $displayLocale = \Locale::getDefault(); } // ==================================================================== diff --git a/src/Symfony/Component/Intl/Data/Provider/LanguageDataProvider.php b/src/Symfony/Component/Intl/Data/Provider/LanguageDataProvider.php index 2178158513e47..ba31087a10212 100644 --- a/src/Symfony/Component/Intl/Data/Provider/LanguageDataProvider.php +++ b/src/Symfony/Component/Intl/Data/Provider/LanguageDataProvider.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Intl\Data\Provider; use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface; -use Symfony\Component\Intl\Locale; /** * Data provider for language-related ICU data. @@ -51,7 +50,7 @@ public function getAliases() public function getName($language, $displayLocale = null) { if (null === $displayLocale) { - $displayLocale = Locale::getDefault(); + $displayLocale = \Locale::getDefault(); } return $this->reader->readEntry($this->path, $displayLocale, ['Names', $language]); @@ -60,7 +59,7 @@ public function getName($language, $displayLocale = null) public function getNames($displayLocale = null) { if (null === $displayLocale) { - $displayLocale = Locale::getDefault(); + $displayLocale = \Locale::getDefault(); } $languages = $this->reader->readEntry($this->path, $displayLocale, ['Names']); diff --git a/src/Symfony/Component/Intl/Data/Provider/LocaleDataProvider.php b/src/Symfony/Component/Intl/Data/Provider/LocaleDataProvider.php index 4328b57d2f09d..df00f64406ae1 100644 --- a/src/Symfony/Component/Intl/Data/Provider/LocaleDataProvider.php +++ b/src/Symfony/Component/Intl/Data/Provider/LocaleDataProvider.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Intl\Data\Provider; use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface; -use Symfony\Component\Intl\Locale; /** * Data provider for locale-related ICU data. @@ -57,7 +56,7 @@ public function getAliases() public function getName($locale, $displayLocale = null) { if (null === $displayLocale) { - $displayLocale = Locale::getDefault(); + $displayLocale = \Locale::getDefault(); } return $this->reader->readEntry($this->path, $displayLocale, ['Names', $locale]); @@ -66,7 +65,7 @@ public function getName($locale, $displayLocale = null) public function getNames($displayLocale = null) { if (null === $displayLocale) { - $displayLocale = Locale::getDefault(); + $displayLocale = \Locale::getDefault(); } $names = $this->reader->readEntry($this->path, $displayLocale, ['Names']); diff --git a/src/Symfony/Component/Intl/Data/Provider/RegionDataProvider.php b/src/Symfony/Component/Intl/Data/Provider/RegionDataProvider.php index 08bd724394d9a..c1ce805f1c154 100644 --- a/src/Symfony/Component/Intl/Data/Provider/RegionDataProvider.php +++ b/src/Symfony/Component/Intl/Data/Provider/RegionDataProvider.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Intl\Data\Provider; use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface; -use Symfony\Component\Intl\Locale; /** * Data provider for region-related ICU data. @@ -46,7 +45,7 @@ public function getRegions() public function getName($region, $displayLocale = null) { if (null === $displayLocale) { - $displayLocale = Locale::getDefault(); + $displayLocale = \Locale::getDefault(); } return $this->reader->readEntry($this->path, $displayLocale, ['Names', $region]); @@ -55,7 +54,7 @@ public function getName($region, $displayLocale = null) public function getNames($displayLocale = null) { if (null === $displayLocale) { - $displayLocale = Locale::getDefault(); + $displayLocale = \Locale::getDefault(); } $names = $this->reader->readEntry($this->path, $displayLocale, ['Names']); diff --git a/src/Symfony/Component/Intl/Data/Provider/ScriptDataProvider.php b/src/Symfony/Component/Intl/Data/Provider/ScriptDataProvider.php index c6844494a89f5..55cd862c5e07d 100644 --- a/src/Symfony/Component/Intl/Data/Provider/ScriptDataProvider.php +++ b/src/Symfony/Component/Intl/Data/Provider/ScriptDataProvider.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Intl\Data\Provider; use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface; -use Symfony\Component\Intl\Locale; /** * Data provider for script-related ICU data. @@ -46,7 +45,7 @@ public function getScripts() public function getName($script, $displayLocale = null) { if (null === $displayLocale) { - $displayLocale = Locale::getDefault(); + $displayLocale = \Locale::getDefault(); } return $this->reader->readEntry($this->path, $displayLocale, ['Names', $script]); @@ -55,7 +54,7 @@ public function getName($script, $displayLocale = null) public function getNames($displayLocale = null) { if (null === $displayLocale) { - $displayLocale = Locale::getDefault(); + $displayLocale = \Locale::getDefault(); } $names = $this->reader->readEntry($this->path, $displayLocale, ['Names']); diff --git a/src/Symfony/Component/Intl/Locale.php b/src/Symfony/Component/Intl/Locale.php index 6e88c5629e00b..9f810235e6f85 100644 --- a/src/Symfony/Component/Intl/Locale.php +++ b/src/Symfony/Component/Intl/Locale.php @@ -31,7 +31,7 @@ final class Locale extends \Locale * The default fallback locale is used as fallback for locales that have no * fallback otherwise. * - * @param string $locale The default fallback locale + * @param string|null $locale The default fallback locale * * @see getFallback() */ @@ -43,7 +43,7 @@ public static function setDefaultFallback($locale) /** * Returns the default fallback locale. * - * @return string The default fallback locale + * @return string|null The default fallback locale * * @see setDefaultFallback() * @see getFallback() @@ -70,7 +70,7 @@ public static function getFallback($locale) if (\function_exists('locale_parse')) { $localeSubTags = locale_parse($locale); if (1 === \count($localeSubTags)) { - if (self::$defaultFallback === $localeSubTags['language']) { + if ('root' !== self::$defaultFallback && self::$defaultFallback === $localeSubTags['language']) { return 'root'; } @@ -98,7 +98,7 @@ public static function getFallback($locale) return substr($locale, 0, $pos); } - if (self::$defaultFallback === $locale) { + if ('root' !== self::$defaultFallback && self::$defaultFallback === $locale) { return 'root'; } diff --git a/src/Symfony/Component/Intl/Resources/bin/common.php b/src/Symfony/Component/Intl/Resources/bin/common.php index 6c63062d4f913..addaa9415ee1a 100644 --- a/src/Symfony/Component/Intl/Resources/bin/common.php +++ b/src/Symfony/Component/Intl/Resources/bin/common.php @@ -68,6 +68,12 @@ function get_icu_version_from_genrb($genrb) return $matches[1]; } +error_reporting(E_ALL); + +set_error_handler(function ($type, $msg, $file, $line) { + throw new \ErrorException($msg, 0, $type, $file, $line); +}); + set_exception_handler(function (\Throwable $exception) { echo "\n"; @@ -82,10 +88,7 @@ function get_icu_version_from_genrb($genrb) echo get_class($cause).': '.$cause->getMessage()."\n"; echo "\n"; echo $cause->getFile().':'.$cause->getLine()."\n"; - foreach ($cause->getTrace() as $trace) { - echo $trace['file'].':'.$trace['line']."\n"; - } - echo "\n"; + echo $cause->getTraceAsString()."\n"; $cause = $cause->getPrevious(); $root = false; diff --git a/src/Symfony/Component/Intl/Resources/bin/update-data.php b/src/Symfony/Component/Intl/Resources/bin/update-data.php index d6e25af474569..0ec098363aa6f 100644 --- a/src/Symfony/Component/Intl/Resources/bin/update-data.php +++ b/src/Symfony/Component/Intl/Resources/bin/update-data.php @@ -11,8 +11,6 @@ use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Intl\Data\Bundle\Compiler\GenrbCompiler; -use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReader; -use Symfony\Component\Intl\Data\Bundle\Reader\JsonBundleReader; use Symfony\Component\Intl\Data\Bundle\Writer\JsonBundleWriter; use Symfony\Component\Intl\Data\Generator\CurrencyDataGenerator; use Symfony\Component\Intl\Data\Generator\GeneratorConfig; @@ -20,9 +18,6 @@ use Symfony\Component\Intl\Data\Generator\LocaleDataGenerator; use Symfony\Component\Intl\Data\Generator\RegionDataGenerator; use Symfony\Component\Intl\Data\Generator\ScriptDataGenerator; -use Symfony\Component\Intl\Data\Provider\LanguageDataProvider; -use Symfony\Component\Intl\Data\Provider\RegionDataProvider; -use Symfony\Component\Intl\Data\Provider\ScriptDataProvider; use Symfony\Component\Intl\Intl; use Symfony\Component\Intl\Locale; use Symfony\Component\Intl\Util\GitRepository; @@ -170,92 +165,40 @@ $compiler = new GenrbCompiler($genrb, $genrbEnv); $config = new GeneratorConfig($sourceDir.'/data', $icuVersionInDownload); +$jsonDir = dirname(__DIR__).'/data'; -$baseDir = dirname(__DIR__).'/data'; - -//$txtDir = $baseDir.'/txt'; -$jsonDir = $baseDir; -//$phpDir = $baseDir.'/'.Intl::PHP; -//$resDir = $baseDir.'/'.Intl::RB_V2; - -$targetDirs = [$jsonDir/*, $resDir*/]; -$workingDirs = [$jsonDir/*, $txtDir, $resDir*/]; - -//$config->addBundleWriter($txtDir, new TextBundleWriter()); $config->addBundleWriter($jsonDir, new JsonBundleWriter()); echo "Starting resource bundle compilation. This may take a while...\n"; -$filesystem->remove($workingDirs); - -foreach ($workingDirs as $targetDir) { - $filesystem->mkdir([ - $targetDir.'/'.Intl::CURRENCY_DIR, - $targetDir.'/'.Intl::LANGUAGE_DIR, - $targetDir.'/'.Intl::LOCALE_DIR, - $targetDir.'/'.Intl::REGION_DIR, - $targetDir.'/'.Intl::SCRIPT_DIR, - ]); -} - // We don't want to use fallback to English during generation -Locale::setDefaultFallback(null); +Locale::setDefaultFallback('root'); echo "Generating language data...\n"; $generator = new LanguageDataGenerator($compiler, Intl::LANGUAGE_DIR); $generator->generateData($config); -//echo "Compiling...\n"; -// -//$compiler->compile($txtDir.'/'.Intl::LANGUAGE_DIR, $resDir.'/'.Intl::LANGUAGE_DIR); - echo "Generating script data...\n"; $generator = new ScriptDataGenerator($compiler, Intl::SCRIPT_DIR); $generator->generateData($config); -//echo "Compiling...\n"; -// -//$compiler->compile($txtDir.'/'.Intl::SCRIPT_DIR, $resDir.'/'.Intl::SCRIPT_DIR); - echo "Generating region data...\n"; $generator = new RegionDataGenerator($compiler, Intl::REGION_DIR); $generator->generateData($config); -//echo "Compiling...\n"; -// -//$compiler->compile($txtDir.'/'.Intl::REGION_DIR, $resDir.'/'.Intl::REGION_DIR); - echo "Generating currency data...\n"; $generator = new CurrencyDataGenerator($compiler, Intl::CURRENCY_DIR); $generator->generateData($config); -//echo "Compiling...\n"; -// -//$compiler->compile($txtDir.'/'.Intl::CURRENCY_DIR, $resDir.'/'.Intl::CURRENCY_DIR); - echo "Generating locale data...\n"; -$reader = new BundleEntryReader(new JsonBundleReader()); - -$generator = new LocaleDataGenerator( - Intl::LOCALE_DIR, - new LanguageDataProvider($jsonDir.'/'.Intl::LANGUAGE_DIR, $reader), - new ScriptDataProvider($jsonDir.'/'.Intl::SCRIPT_DIR, $reader), - new RegionDataProvider($jsonDir.'/'.Intl::REGION_DIR, $reader) -); - +$generator = new LocaleDataGenerator($compiler, Intl::LOCALE_DIR); $generator->generateData($config); -//echo "Compiling...\n"; -// -//$compiler->compile($txtDir.'/'.Intl::LOCALE_DIR, $resDir.'/'.Intl::LOCALE_DIR); -// -//$filesystem->remove($txtDir); - echo "Resource bundle compilation complete.\n"; $gitInfo = << @@ -631,7 +630,7 @@ public function testGetNames($displayLocale) public function testGetNamesDefaultLocale() { - Locale::setDefault('de_AT'); + \Locale::setDefault('de_AT'); $this->assertSame( $this->dataProvider->getNames('de_AT'), @@ -670,7 +669,7 @@ public function testGetName($displayLocale) public function testGetNameDefaultLocale() { - Locale::setDefault('de_AT'); + \Locale::setDefault('de_AT'); $expected = $this->dataProvider->getNames('de_AT'); $actual = []; diff --git a/src/Symfony/Component/Intl/Tests/Data/Provider/AbstractDataProviderTest.php b/src/Symfony/Component/Intl/Tests/Data/Provider/AbstractDataProviderTest.php index d0ef4d273b45a..562f8386d1c9e 100644 --- a/src/Symfony/Component/Intl/Tests/Data/Provider/AbstractDataProviderTest.php +++ b/src/Symfony/Component/Intl/Tests/Data/Provider/AbstractDataProviderTest.php @@ -703,7 +703,7 @@ abstract class AbstractDataProviderTest extends TestCase protected function setUp() { - Locale::setDefault('en'); + \Locale::setDefault('en'); Locale::setDefaultFallback('en'); } diff --git a/src/Symfony/Component/Intl/Tests/Data/Provider/AbstractLanguageDataProviderTest.php b/src/Symfony/Component/Intl/Tests/Data/Provider/AbstractLanguageDataProviderTest.php index 003e688d03d87..2b55349584a62 100644 --- a/src/Symfony/Component/Intl/Tests/Data/Provider/AbstractLanguageDataProviderTest.php +++ b/src/Symfony/Component/Intl/Tests/Data/Provider/AbstractLanguageDataProviderTest.php @@ -13,7 +13,6 @@ use Symfony\Component\Intl\Data\Provider\LanguageDataProvider; use Symfony\Component\Intl\Intl; -use Symfony\Component\Intl\Locale; /** * @author Bernhard Schussek @@ -865,7 +864,7 @@ public function testGetNames($displayLocale) public function testGetNamesDefaultLocale() { - Locale::setDefault('de_AT'); + \Locale::setDefault('de_AT'); $this->assertSame( $this->dataProvider->getNames('de_AT'), @@ -901,7 +900,7 @@ public function testGetName($displayLocale) public function testGetNameDefaultLocale() { - Locale::setDefault('de_AT'); + \Locale::setDefault('de_AT'); $names = $this->dataProvider->getNames('de_AT'); diff --git a/src/Symfony/Component/Intl/Tests/Data/Provider/AbstractLocaleDataProviderTest.php b/src/Symfony/Component/Intl/Tests/Data/Provider/AbstractLocaleDataProviderTest.php index aeca40cdbd6cd..88242a6f9bcb3 100644 --- a/src/Symfony/Component/Intl/Tests/Data/Provider/AbstractLocaleDataProviderTest.php +++ b/src/Symfony/Component/Intl/Tests/Data/Provider/AbstractLocaleDataProviderTest.php @@ -13,7 +13,6 @@ use Symfony\Component\Intl\Data\Provider\LocaleDataProvider; use Symfony\Component\Intl\Intl; -use Symfony\Component\Intl\Locale; /** * @author Bernhard Schussek @@ -64,7 +63,7 @@ public function testGetNames($displayLocale) public function testGetNamesDefaultLocale() { - Locale::setDefault('de_AT'); + \Locale::setDefault('de_AT'); $this->assertSame( $this->dataProvider->getNames('de_AT'), @@ -100,7 +99,7 @@ public function testGetName($displayLocale) public function testGetNameDefaultLocale() { - Locale::setDefault('de_AT'); + \Locale::setDefault('de_AT'); $names = $this->dataProvider->getNames('de_AT'); diff --git a/src/Symfony/Component/Intl/Tests/Data/Provider/AbstractRegionDataProviderTest.php b/src/Symfony/Component/Intl/Tests/Data/Provider/AbstractRegionDataProviderTest.php index 1f5febb2b86bf..aeb922f9e3e5f 100644 --- a/src/Symfony/Component/Intl/Tests/Data/Provider/AbstractRegionDataProviderTest.php +++ b/src/Symfony/Component/Intl/Tests/Data/Provider/AbstractRegionDataProviderTest.php @@ -13,7 +13,6 @@ use Symfony\Component\Intl\Data\Provider\RegionDataProvider; use Symfony\Component\Intl\Intl; -use Symfony\Component\Intl\Locale; /** * @author Bernhard Schussek @@ -316,7 +315,7 @@ public function testGetNames($displayLocale) public function testGetNamesDefaultLocale() { - Locale::setDefault('de_AT'); + \Locale::setDefault('de_AT'); $this->assertSame( $this->dataProvider->getNames('de_AT'), diff --git a/src/Symfony/Component/Intl/Tests/Data/Provider/AbstractScriptDataProviderTest.php b/src/Symfony/Component/Intl/Tests/Data/Provider/AbstractScriptDataProviderTest.php index db4b81ebdb13f..8620fb2060fbc 100644 --- a/src/Symfony/Component/Intl/Tests/Data/Provider/AbstractScriptDataProviderTest.php +++ b/src/Symfony/Component/Intl/Tests/Data/Provider/AbstractScriptDataProviderTest.php @@ -13,7 +13,6 @@ use Symfony\Component\Intl\Data\Provider\ScriptDataProvider; use Symfony\Component\Intl\Intl; -use Symfony\Component\Intl\Locale; /** * @author Bernhard Schussek @@ -255,7 +254,7 @@ public function testGetNames($displayLocale) public function testGetNamesDefaultLocale() { - Locale::setDefault('de_AT'); + \Locale::setDefault('de_AT'); $this->assertSame( $this->dataProvider->getNames('de_AT'), @@ -291,7 +290,7 @@ public function testGetName($displayLocale) public function testGetNameDefaultLocale() { - Locale::setDefault('de_AT'); + \Locale::setDefault('de_AT'); $names = $this->dataProvider->getNames('de_AT'); diff --git a/src/Symfony/Component/Intl/Tests/LocaleTest.php b/src/Symfony/Component/Intl/Tests/LocaleTest.php index ff908cbcabd00..fd998612a7eae 100644 --- a/src/Symfony/Component/Intl/Tests/LocaleTest.php +++ b/src/Symfony/Component/Intl/Tests/LocaleTest.php @@ -46,4 +46,28 @@ public function testGetFallback($expected, $locale) { $this->assertSame($expected, Locale::getFallback($locale)); } + + public function testNoDefaultFallback() + { + $prev = Locale::getDefaultFallback(); + Locale::setDefaultFallback(null); + + $this->assertSame('nl', Locale::getFallback('nl_NL')); + $this->assertNull(Locale::getFallback('nl')); + $this->assertNull(Locale::getFallback('root')); + + Locale::setDefaultFallback($prev); + } + + public function testDefaultRootFallback() + { + $prev = Locale::getDefaultFallback(); + Locale::setDefaultFallback('root'); + + $this->assertSame('nl', Locale::getFallback('nl_NL')); + $this->assertSame('root', Locale::getFallback('nl')); + $this->assertNull(Locale::getFallback('root')); + + Locale::setDefaultFallback($prev); + } } diff --git a/src/Symfony/Component/Ldap/Exception/ConnectionException.php b/src/Symfony/Component/Ldap/Exception/ConnectionException.php index cded4cf2a389a..7fa8e89f6f4a2 100644 --- a/src/Symfony/Component/Ldap/Exception/ConnectionException.php +++ b/src/Symfony/Component/Ldap/Exception/ConnectionException.php @@ -12,7 +12,7 @@ namespace Symfony\Component\Ldap\Exception; /** - * ConnectionException is throw if binding to ldap can not be established. + * ConnectionException is thrown if binding to ldap can not be established. * * @author Grégoire Pineau */ diff --git a/src/Symfony/Component/Ldap/Exception/DriverNotFoundException.php b/src/Symfony/Component/Ldap/Exception/DriverNotFoundException.php index 40258435bb6a7..382cdf5ca6686 100644 --- a/src/Symfony/Component/Ldap/Exception/DriverNotFoundException.php +++ b/src/Symfony/Component/Ldap/Exception/DriverNotFoundException.php @@ -12,7 +12,7 @@ namespace Symfony\Component\Ldap\Exception; /** - * LdapException is throw if php ldap module is not loaded. + * LdapException is thrown if php ldap module is not loaded. * * @author Charles Sarrazin */ diff --git a/src/Symfony/Component/Ldap/Exception/LdapException.php b/src/Symfony/Component/Ldap/Exception/LdapException.php index 4045f32cf44b5..df8eabfbcba88 100644 --- a/src/Symfony/Component/Ldap/Exception/LdapException.php +++ b/src/Symfony/Component/Ldap/Exception/LdapException.php @@ -12,7 +12,7 @@ namespace Symfony\Component\Ldap\Exception; /** - * LdapException is throw if php ldap module is not loaded. + * LdapException is thrown if php ldap module is not loaded. * * @author Grégoire Pineau */ diff --git a/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/EntryManagerTest.php b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/EntryManagerTest.php new file mode 100644 index 0000000000000..0617762ed6f08 --- /dev/null +++ b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/EntryManagerTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Ldap\Tests\Adapter\ExtLdap; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Ldap\Adapter\ExtLdap\Connection; +use Symfony\Component\Ldap\Adapter\ExtLdap\EntryManager; +use Symfony\Component\Ldap\Entry; + +class EntryManagerTest extends TestCase +{ + /** + * @expectedException \Symfony\Component\Ldap\Exception\NotBoundException + * @expectedExceptionMessage Query execution is not possible without binding the connection first. + */ + public function testGetResources() + { + $connection = $this->getMockBuilder(Connection::class)->getMock(); + $connection + ->expects($this->once()) + ->method('isBound')->willReturn(false); + + $entry = new Entry('$$$$$$'); + $entryManager = new EntryManager($connection); + $entryManager->update($entry); + } +} diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index 75395b66c587e..68cc6c65ae5a9 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -427,6 +427,7 @@ public function wait(callable $callback = null) } while ($running); while ($this->isRunning()) { + $this->checkTimeout(); usleep(1000); } diff --git a/src/Symfony/Component/Process/Tests/ErrorProcessInitiator.php b/src/Symfony/Component/Process/Tests/ErrorProcessInitiator.php new file mode 100755 index 0000000000000..c37aeb5c8ffd9 --- /dev/null +++ b/src/Symfony/Component/Process/Tests/ErrorProcessInitiator.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Tests; + +use Symfony\Component\Process\Exception\ProcessTimedOutException; +use Symfony\Component\Process\Process; + +require \dirname(__DIR__).'/vendor/autoload.php'; + +list('e' => $php) = getopt('e:') + ['e' => 'php']; + +try { + $process = new Process("exec $php -r \"echo 'ready'; trigger_error('error', E_USER_ERROR);\""); + $process->start(); + $process->setTimeout(0.5); + while (false === strpos($process->getOutput(), 'ready')) { + usleep(1000); + } + $process->signal(SIGSTOP); + $process->wait(); + + return $process->getExitCode(); +} catch (ProcessTimedOutException $t) { + echo $t->getMessage().PHP_EOL; + + return 1; +} diff --git a/src/Symfony/Component/Process/Tests/ProcessTest.php b/src/Symfony/Component/Process/Tests/ProcessTest.php index a9ecbe8043f64..18fef4ff5ff17 100644 --- a/src/Symfony/Component/Process/Tests/ProcessTest.php +++ b/src/Symfony/Component/Process/Tests/ProcessTest.php @@ -1551,6 +1551,15 @@ public function testEnvArgument() $this->assertSame($env, $p->getEnv()); } + public function testWaitStoppedDeadProcess() + { + $process = $this->getProcess(self::$phpBin.' '.__DIR__.'/ErrorProcessInitiator.php -e '.self::$phpBin); + $process->start(); + $process->setTimeout(2); + $process->wait(); + $this->assertFalse($process->isRunning()); + } + /** * @param string $commandline * @param string|null $cwd diff --git a/src/Symfony/Component/PropertyInfo/README.md b/src/Symfony/Component/PropertyInfo/README.md index 5ac21f384e59b..1cf30318deb8b 100644 --- a/src/Symfony/Component/PropertyInfo/README.md +++ b/src/Symfony/Component/PropertyInfo/README.md @@ -7,6 +7,7 @@ of popular sources. Resources --------- + * [Documentation](https://symfony.com/doc/current/components/property_info.html) * [Contributing](https://symfony.com/doc/current/contributing/index.html) * [Report issues](https://github.com/symfony/symfony/issues) and [send Pull Requests](https://github.com/symfony/symfony/pulls) diff --git a/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php b/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php index 6a439e710acf5..6b8a50efb7317 100644 --- a/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php +++ b/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php @@ -199,7 +199,8 @@ public function getResolver() */ protected function getDefaultRouteName(\ReflectionClass $class, \ReflectionMethod $method) { - $name = strtolower(str_replace('\\', '_', $class->name).'_'.$method->name); + $name = str_replace('\\', '_', $class->name).'_'.$method->name; + $name = \function_exists('mb_strtolower') && preg_match('//u', $name) ? mb_strtolower($name, 'UTF-8') : strtolower($name); if ($this->defaultRouteIndex > 0) { $name .= '_'.$this->defaultRouteIndex; } diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/EncodingClass.php b/src/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/EncodingClass.php new file mode 100644 index 0000000000000..dac72e3ddcfca --- /dev/null +++ b/src/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/EncodingClass.php @@ -0,0 +1,10 @@ +assertEquals(array_merge($classRouteData['methods'], $methodRouteData['methods']), $route->getMethods(), '->load merges class and method route methods'); } + /** + * @requires function mb_strtolower + */ + public function testDefaultRouteName() + { + $methodRouteData = [ + 'name' => null, + ]; + + $this->reader + ->expects($this->once()) + ->method('getMethodAnnotations') + ->will($this->returnValue([$this->getAnnotatedRoute($methodRouteData)])) + ; + + $routeCollection = $this->loader->load('Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\EncodingClass'); + $defaultName = array_keys($routeCollection->all())[0]; + + $this->assertSame($defaultName, 'symfony_component_routing_tests_fixtures_annotatedclasses_encodingclass_routeàction'); + } + private function getAnnotatedRoute($data) { return new Route($data); diff --git a/src/Symfony/Component/Routing/Tests/Loader/AnnotationDirectoryLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/AnnotationDirectoryLoaderTest.php index ac25493481ef6..9465ef05df23c 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/AnnotationDirectoryLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/AnnotationDirectoryLoaderTest.php @@ -29,7 +29,7 @@ protected function setUp() public function testLoad() { - $this->reader->expects($this->exactly(3))->method('getClassAnnotation'); + $this->reader->expects($this->exactly(4))->method('getClassAnnotation'); $this->reader ->expects($this->any()) @@ -52,6 +52,7 @@ public function testLoadIgnoresHiddenDirectories() 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass', 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BazClass', 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\FooClass', + 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\EncodingClass', ]); $this->reader diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.be.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.be.xlf new file mode 100644 index 0000000000000..c3cd7911e55cb --- /dev/null +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.be.xlf @@ -0,0 +1,67 @@ + + + + + + An authentication exception occurred. + Памылка аўтэнтыфікацыі. + + + Authentication credentials could not be found. + Дадзеныя аўтэнтыфікацыі не знойдзены. + + + Authentication request could not be processed due to a system problem. + Запыт аўтэнтыфікацыі не можа быць апрацаваны ў сувязі з праблемай у сістэме. + + + Invalid credentials. + Несапраўдныя дадзеныя аўтэнтыфікацыі. + + + Cookie has already been used by someone else. + Нехта іншы ўжо выкарыстаў гэтыя кукі (cookie). + + + Not privileged to request the resource. + Адсутнічаюць правы на запыт гэтага рэсурсу. + + + Invalid CSRF token. + Несапраўдны CSRF-токен. + + + No authentication provider found to support the authentication token. + Не знойдзен правайдар аўтэнтыфікацыі, які можа падтрымліваць гэты токен аўтэнтыфікацыі. + + + No session available, it either timed out or cookies are not enabled. + Сесія не даступна, яе час скончыўся, або кукі (cookies) выключаны. + + + No token could be found. + Токен не знойдзен. + + + Username could not be found. + Імя карыстальніка не знойдзена. + + + Account has expired. + Скончыўся тэрмін дзеяння акаўнта. + + + Credentials have expired. + Скончыўся тэрмін дзеяння дадзеных аўтэнтыфікацыі. + + + Account is disabled. + Акаўнт адключан. + + + Account is locked. + Акаўнт заблакіраван. + + + + diff --git a/src/Symfony/Component/Security/Http/Authorization/AccessDeniedHandlerInterface.php b/src/Symfony/Component/Security/Http/Authorization/AccessDeniedHandlerInterface.php index aea901181f601..871c877f57295 100644 --- a/src/Symfony/Component/Security/Http/Authorization/AccessDeniedHandlerInterface.php +++ b/src/Symfony/Component/Security/Http/Authorization/AccessDeniedHandlerInterface.php @@ -26,7 +26,7 @@ interface AccessDeniedHandlerInterface /** * Handles an access denied failure. * - * @return Response may return null + * @return Response|null */ public function handle(Request $request, AccessDeniedException $accessDeniedException); } diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index c5bd3ffd97202..00bd8a2a52faf 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -354,7 +354,13 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref throw new RuntimeException(sprintf('Cannot create an instance of %s from serialized data because the variadic parameter %s can only accept an array.', $class, $constructorParameter->name)); } - $params = array_merge($params, $data[$paramName]); + $variadicParameters = []; + foreach ($data[$paramName] as $parameterData) { + $variadicParameters[] = $this->denormalizeParameter($reflectionClass, $constructorParameter, $paramName, $parameterData, $context, $format); + } + + $params = array_merge($params, $variadicParameters); + unset($data[$key]); } } elseif ($allowed && !$ignored && (isset($data[$key]) || \array_key_exists($key, $data))) { $parameterData = $data[$key]; diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/VariadicConstructorTypedArgsDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/VariadicConstructorTypedArgsDummy.php new file mode 100644 index 0000000000000..9c6fd980a1524 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/VariadicConstructorTypedArgsDummy.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Fixtures; + +class VariadicConstructorTypedArgsDummy +{ + private $foo; + + public function __construct(Dummy ...$foo) + { + $this->foo = $foo; + } + + public function getFoo() + { + return $this->foo; + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractNormalizerTest.php index 890cb27019309..cce383075a6fe 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractNormalizerTest.php @@ -8,11 +8,15 @@ use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; +use Symfony\Component\Serializer\Normalizer\PropertyNormalizer; +use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Tests\Fixtures\AbstractNormalizerDummy; +use Symfony\Component\Serializer\Tests\Fixtures\Dummy; use Symfony\Component\Serializer\Tests\Fixtures\NullableConstructorArgumentDummy; use Symfony\Component\Serializer\Tests\Fixtures\ProxyDummy; use Symfony\Component\Serializer\Tests\Fixtures\StaticConstructorDummy; use Symfony\Component\Serializer\Tests\Fixtures\StaticConstructorNormalizer; +use Symfony\Component\Serializer\Tests\Fixtures\VariadicConstructorTypedArgsDummy; /** * Provides a dummy Normalizer which extends the AbstractNormalizer. @@ -128,4 +132,21 @@ public function testObjectWithNullableConstructorArgument() $this->assertNull($dummy->getFoo()); } + + /** + * @requires PHP 5.6 + */ + public function testObjectWithVariadicConstructorTypedArguments() + { + $normalizer = new PropertyNormalizer(); + $normalizer->setSerializer(new Serializer([$normalizer])); + $data = ['foo' => [['foo' => 'Foo', 'bar' => 'Bar', 'baz' => 'Baz', 'qux' => 'Qux'], ['foo' => 'FOO', 'bar' => 'BAR', 'baz' => 'BAZ', 'qux' => 'QUX']]]; + $dummy = $normalizer->denormalize($data, VariadicConstructorTypedArgsDummy::class); + + $this->assertInstanceOf(VariadicConstructorTypedArgsDummy::class, $dummy); + $this->assertCount(2, $dummy->getFoo()); + foreach ($dummy->getFoo() as $foo) { + $this->assertInstanceOf(Dummy::class, $foo); + } + } } diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.be.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.be.xlf new file mode 100644 index 0000000000000..ab3845ee20153 --- /dev/null +++ b/src/Symfony/Component/Validator/Resources/translations/validators.be.xlf @@ -0,0 +1,367 @@ + + + + + + This value should be false. + Значэнне павінна быць Не. + + + This value should be true. + Значэнне павінна быць Так. + + + This value should be of type {{ type }}. + Тып значэння павінен быць {{ type }}. + + + This value should be blank. + Значэнне павінна быць пустым. + + + 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 }} варыянтаў. + + + You must select at most {{ limit }} choice.|You must select at most {{ limit }} choices. + Вы павінны выбраць не больш за {{ limit }} варыянт.|Вы павінны выбраць не больш за {{ limit }} варыянтаў. + + + One or more of the given values is invalid. + Адзін або некалькі пазначаных значэнняў з'яўляецца несапраўдным. + + + This field was not expected. + Гэта поле не чакаецца. + + + This field is missing. + Гэта поле адсутнічае. + + + This value is not a valid date. + Гэта значэнне не з'яўляецца карэктнай датай. + + + This value is not a valid datetime. + Гэта значэнне не з'яўляецца карэктнай датай i часом. + + + This value is not a valid email address. + Гэта значэнне не з'яўляецца карэктным адрасам электроннай пошты. + + + The file could not be found. + Файл не знойдзен. + + + The file is not readable. + Файл не чытаецца. + + + The file is too large ({{ size }} {{ suffix }}). Allowed maximum size is {{ limit }} {{ suffix }}. + Файл занадта вялікі ({{ size }} {{ suffix }}). Максімальна дазволены памер {{ limit }} {{ suffix }}. + + + The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }}. + MIME-тып файлу некарэкты ({{ type }}). Дазволеныя MIME-тыпы файлу {{ types }}. + + + This value should be {{ limit }} or less. + Значэнне павінна быць {{ limit }} або менш. + + + 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 }} сімвалаў або менш. + + + This value should be {{ limit }} or more. + Значэнне павінна быць {{ limit }} або больш. + + + 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 }} сімвалаў. + + + This value should not be blank. + Значэнне не павінна быць пустым. + + + This value should not be null. + Значэнне не павінна быць null. + + + This value should be null. + Значэнне павінна быць null. + + + This value is not valid. + Значэнне з'яўляецца не сапраўдным. + + + This value is not a valid time. + Значэнне не з'яўляецца сапраўдным часам. + + + This value is not a valid URL. + Значэнне не з'яўляецца сапраўдным URL-адрасам. + + + The two values should be equal. + Абодва значэнні павінны быць аднолькавымі. + + + The file is too large. Allowed maximum size is {{ limit }} {{ suffix }}. + Файл занадта вялікі. Максімальна дазволены памер {{ limit }} {{ suffix }}. + + + The file is too large. + Файл занадта вялікі. + + + The file could not be uploaded. + Немагчыма запампаваць файл. + + + This value should be a valid number. + Значэнне павінна быць лікам. + + + This file is not a valid image. + Гэты файл не з'яўляецца сапраўднай выявай. + + + This is not a valid IP address. + Значэнне не з'яўляецца сапраўдным IP-адрасам. + + + This value is not a valid language. + Значэнне не з'яўляецца сапраўдным мовай. + + + This value is not a valid 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. + + + The image width is too small ({{ width }}px). Minimum width expected is {{ min_width }}px. + Гэта выява занадта маленькая ({{ width }}px). Дазваляецца мінімальная шырыня {{ min_width }}px. + + + The image height is too big ({{ height }}px). Allowed maximum height is {{ max_height }}px. + Гэты выява занадта вялікая ({{ width }}px). Дазваляецца максімальная вышыня {{ max_width }}px. + + + The image height is too small ({{ height }}px). Minimum height expected is {{ min_height }}px. + Гэта выява занадта маленькая ({{ width }}px). Дазваляецца мінімальная вышыня {{ min_width }}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 }} сімвалаў. + + + The file was only partially uploaded. + Файл быў запампаваны толькі часткова. + + + No file was uploaded. + Файл не быў запампаваны. + + + No temporary folder was configured in php.ini. + У php.ini не была налажана часовая папка, або часовая папка не існуе. + + + Cannot write temporary file to disk. + Немагчыма запісаць часовы файл на дыск. + + + A PHP extension caused the upload to fail. + Пашырэнне PHP выклікала памылку загрузкі. + + + This collection should contain {{ limit }} element or more.|This collection should contain {{ limit }} elements or more. + Калекцыя павінна змяшчаць прынамсі {{ limit }} элемент.|Калекцыя павінна змяшчаць прынамсі {{ limit }} элементаў. + + + This collection should contain {{ limit }} element or less.|This collection should contain {{ limit }} elements or less. + Калекцыя павінна змяшчаць {{ limit }} або менш элемент.|Калекцыя павінна змяшчаць {{ limit }} або менш элементаў. + + + This collection should contain exactly {{ limit }} element.|This collection should contain exactly {{ limit }} elements. + Калекцыя павінна змяшчаць роўна {{ limit }} элемент.|Калекцыя павінна змяшчаць роўна {{ limit }} элементаў. + + + Invalid card number. + Несапраўдны нумар карты. + + + Unsupported card type or invalid card number. + Тып карты не падтрымліваецца або несапраўдны нумар карты. + + + This is not a valid International Bank Account Number (IBAN). + Несапраўдны міжнародны нумар банкаўскага рахунку (IBAN). + + + This value is not a valid ISBN-10. + Гэта значэнне не з'яўляецца сапраўдным ISBN-10. + + + This value is not a valid ISBN-13. + Гэта значэнне не з'яўляецца сапраўдным ISBN-13. + + + This value is neither a valid ISBN-10 nor a valid ISBN-13. + Гэта значэнне не з'яўляецца сапраўдным ISBN-10 або ISBN-13. + + + This value is not a valid ISSN. + Гэта значэнне не з'яўляецца сапраўдным ISSN. + + + This value is not a valid currency. + Гэта значэнне не з'яўляецца сапраўднай валютай. + + + This value should be equal to {{ compared_value }}. + Значэнне павінна раўняцца {{ compared_value }}. + + + This value should be greater than {{ compared_value }}. + Значэнне павінна быць больш чым {{ compared_value }}. + + + This value should be greater than or equal to {{ compared_value }}. + Значэнне павінна быць больш чым або раўняцца {{ compared_value }}. + + + This value should be identical to {{ compared_value_type }} {{ compared_value }}. + Значэнне павінна быць ідэнтычным {{ compared_value_type }} {{ compared_value }}. + + + This value should be less than {{ compared_value }}. + Значэнне павінна быць менш чым {{ compared_value }}. + + + This value should be less than or equal to {{ compared_value }}. + Значэнне павінна быць менш чым або раўняцца {{ compared_value }}. + + + This value should not be equal to {{ compared_value }}. + Значэнне не павінна раўняцца {{ compared_value }}. + + + This value should not be identical to {{ 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}} . + + + The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}. + Суадносіны бакоў выявы з'яўляецца занадта маленькімі ({{ ratio }}). Дазваляецца мінімальныя суадносіны {{ min_ratio }}. + + + The image is square ({{ width }}x{{ height }}px). Square images are not allowed. + Выява квадратная ({{width}}x{{height}}px). Квадратныя выявы не дазволены. + + + The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed. + Выява ў альбомнай арыентацыі ({{ width }}x{{ height }}px). Выявы ў альбомнай арыентацыі не дазволены. + + + The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed. + Выява ў партрэтнай арыентацыі ({{ width }}x{{ height }}px). Выявы ў партрэтнай арыентацыі не дазволены. + + + An empty file is not allowed. + Пусты файл не дазволены. + + + The host could not be resolved. + Не магчыма знайсці імя хоста. + + + This value does not match the expected {{ charset }} charset. + Гэта значэнне не супадае з чаканай {{ charset }} кадыроўкай. + + + This is not a valid Business Identifier Code (BIC). + Несапраўдны банкаўскі ідэнтыфікацыйны код (BIC). + + + Error + Памылка + + + This is not a valid UUID. + Гэта несапраўдны UUID. + + + This value should be a multiple of {{ compared_value }}. + Значэнне павінна быць кратным {{ compared_value }}. + + + This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}. + Банкаўскі ідэнтыфікацыйны код (BIC) не звязан з IBAN {{ iban }}. + + + This value should be valid JSON. + Гэта значэнне павінна быць у фармаце JSON. + + + This collection should contain only unique elements. + Калекцыя павінна змяшчаць толькі ўнікальныя элементы. + + + This value should be positive. + Значэнне павінна быць дадатным. + + + This value should be either positive or zero. + Значэнне павінна быць дадатным ці нуль. + + + This value should be negative. + Значэнне павінна быць адмоўным. + + + This value should be either negative or zero. + Значэнне павінна быць адмоўным ці нуль. + + + This value is not a valid timezone. + Значэнне не з'яўляецца сапраўдным гадзінным поясам. + + + This password has been leaked in a data breach, it must not be used. Please use another password. + Гэты пароль быў выкрадзены ў выніку ўзлому дадзеных, таму яго нельга выкарыстоўваць. Калі ласка, выкарыстоўвайце іншы пароль. + + + + diff --git a/src/Symfony/Component/Workflow/Tests/WorkflowTest.php b/src/Symfony/Component/Workflow/Tests/WorkflowTest.php index 68ebaafcd9aa1..d6f6bae45cbf1 100644 --- a/src/Symfony/Component/Workflow/Tests/WorkflowTest.php +++ b/src/Symfony/Component/Workflow/Tests/WorkflowTest.php @@ -304,6 +304,45 @@ public function testApplyWithEventDispatcher() $this->assertSame($eventNameExpected, $eventDispatcher->dispatchedEvents); } + public function testApplyDoesNotTriggerExtraGuardWithEventDispatcher() + { + $transitions[] = new Transition('a-b', 'a', 'b'); + $transitions[] = new Transition('a-c', 'a', 'c'); + $definition = new Definition(['a', 'b', 'c'], $transitions); + + $subject = new \stdClass(); + $subject->marking = null; + $eventDispatcher = new EventDispatcherMock(); + $workflow = new Workflow($definition, new MultipleStateMarkingStore(), $eventDispatcher, 'workflow_name'); + + $eventNameExpected = [ + 'workflow.guard', + 'workflow.workflow_name.guard', + 'workflow.workflow_name.guard.a-b', + 'workflow.leave', + 'workflow.workflow_name.leave', + 'workflow.workflow_name.leave.a', + 'workflow.transition', + 'workflow.workflow_name.transition', + 'workflow.workflow_name.transition.a-b', + 'workflow.enter', + 'workflow.workflow_name.enter', + 'workflow.workflow_name.enter.b', + 'workflow.entered', + 'workflow.workflow_name.entered', + 'workflow.workflow_name.entered.b', + 'workflow.completed', + 'workflow.workflow_name.completed', + 'workflow.workflow_name.completed.a-b', + 'workflow.announce', + 'workflow.workflow_name.announce', + ]; + + $marking = $workflow->apply($subject, 'a-b'); + + $this->assertSame($eventNameExpected, $eventDispatcher->dispatchedEvents); + } + public function testEventName() { $definition = $this->createComplexWorkflowDefinition(); diff --git a/src/Symfony/Component/Workflow/Workflow.php b/src/Symfony/Component/Workflow/Workflow.php index a0500b3796bf3..18ca7f7969028 100644 --- a/src/Symfony/Component/Workflow/Workflow.php +++ b/src/Symfony/Component/Workflow/Workflow.php @@ -125,22 +125,20 @@ public function can($subject, $transitionName) */ public function apply($subject, $transitionName) { - $transitions = $this->getEnabledTransitions($subject); - - // We can shortcut the getMarking method in order to boost performance, - // since the "getEnabledTransitions" method already checks the Marking - // state - $marking = $this->markingStore->getMarking($subject); - - $applied = false; + $marking = $this->getMarking($subject); + $transitions = []; - foreach ($transitions as $transition) { - if ($transitionName !== $transition->getName()) { - continue; + foreach ($this->definition->getTransitions() as $transition) { + if ($transitionName === $transition->getName() && $this->doCan($subject, $marking, $transition)) { + $transitions[] = $transition; } + } - $applied = true; + if (!$transitions) { + throw new LogicException(sprintf('Unable to apply transition "%s" for workflow "%s".', $transitionName, $this->name)); + } + foreach ($transitions as $transition) { $this->leave($subject, $transition, $marking); $this->transition($subject, $transition, $marking); @@ -156,10 +154,6 @@ public function apply($subject, $transitionName) $this->announce($subject, $transition, $marking); } - if (!$applied) { - throw new LogicException(sprintf('Unable to apply transition "%s" for workflow "%s".', $transitionName, $this->name)); - } - return $marking; }