diff --git a/.appveyor.yml b/.appveyor.yml index e0d8c17dc1f56..1a91d001f0bee 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -48,7 +48,7 @@ install: - php composer.phar global require --no-progress --no-scripts --no-plugins symfony/flex - git config --global user.email "" - git config --global user.name "Symfony" - - FOR /F "tokens=* USEBACKQ" %%F IN (`bash -c "grep branch-version composer.json | grep -o '[0-9.]*'"`) DO (SET SYMFONY_VERSION=%%F) + - FOR /F "tokens=* USEBACKQ" %%F IN (`bash -c "grep branch-version composer.json | grep -o '[0-9.x]*'"`) DO (SET SYMFONY_VERSION=%%F) - php .github/build-packages.php "HEAD^" %SYMFONY_VERSION% src\Symfony\Bridge\PhpUnit src\Symfony\Contracts - SET "SYMFONY_REQUIRE=>=%SYMFONY_VERSION%" - SET COMPOSER_ROOT_VERSION=%SYMFONY_VERSION%.x-dev diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index df9db03fa8bca..c113ccbf96f1b 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,6 @@ | Q | A | ------------- | --- -| Branch? | 5.x for features / 3.4, 4.4 or 5.1 for bug fixes +| Branch? | 5.x for features / 4.4 or 5.1 for bug fixes | Bug fix? | yes/no | New feature? | yes/no | Deprecations? | yes/no diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2bcc279d84cf2..b9f5438e2e061 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,6 +15,17 @@ jobs: php: ['7.2', '7.4'] services: + ldap: + image: bitnami/openldap + ports: + - 3389:3389 + env: + LDAP_ADMIN_USERNAME: admin + LDAP_ADMIN_PASSWORD: symfony + LDAP_ROOT: dc=symfony,dc=com + LDAP_PORT_NUMBER: 3389 + LDAP_USERS: a + LDAP_PASSWORDS: a redis: image: redis:6.0.0 ports: @@ -29,9 +40,16 @@ jobs: - 7004:7004 - 7005:7005 - 7006:7006 - - 7007:7007 env: - STANDALONE: true + STANDALONE: 1 + redis-sentinel: + image: bitnami/redis-sentinel:6.0 + ports: + - 26379:26379 + env: + REDIS_MASTER_HOST: redis + REDIS_MASTER_SET: redis_sentinel + REDIS_SENTINEL_QUORUM: 1 memcached: image: memcached:1.6.5 ports: @@ -75,17 +93,12 @@ jobs: - name: Install system dependencies run: | - echo "::group::add apt sources" - sudo wget -O - http://packages.couchbase.com/ubuntu/couchbase.key | sudo apt-key add - - echo "deb http://packages.couchbase.com/ubuntu bionic bionic/main" | sudo tee /etc/apt/sources.list.d/couchbase.list - echo "::endgroup::" - echo "::group::apt-get update" sudo apt-get update echo "::endgroup::" echo "::group::install tools & libraries" - sudo apt-get install libcouchbase-dev librdkafka-dev + sudo apt-get install librdkafka-dev echo "::endgroup::" - name: Configure Couchbase @@ -99,16 +112,27 @@ jobs: uses: shivammathur/setup-php@v2 with: coverage: "none" - extensions: "json,couchbase,memcached,mongodb,redis,rdkafka,xsl" + extensions: "json,couchbase,memcached,mongodb,redis,rdkafka,xsl,ldap" ini-values: "memory_limit=-1" php-version: "${{ matrix.php }}" tools: pecl + - name: Display versions + run: | + php -r 'foreach (get_loaded_extensions() as $extension) echo $extension . " " . phpversion($extension) . PHP_EOL;' + php -i + + - name: Load fixtures + uses: docker://bitnami/openldap + with: + entrypoint: /bin/bash + args: -c "(ldapwhoami -h localhost:3389 -D cn=admin,dc=symfony,dc=com -w symfony||sleep 5) && ldapadd -h ldap:3389 -D cn=admin,dc=symfony,dc=com -w symfony -f src/Symfony/Component/Ldap/Tests/Fixtures/data/fixtures.ldif && ldapdelete -h ldap:3389 -D cn=admin,dc=symfony,dc=com -w symfony cn=a,ou=users,dc=symfony,dc=com" + - name: Configure composer run: | COMPOSER_HOME="$(composer config home)" ([ -d "$COMPOSER_HOME" ] || mkdir "$COMPOSER_HOME") && cp .github/composer-config.json "$COMPOSER_HOME/config.json" - echo "COMPOSER_ROOT_VERSION=$(grep branch-version composer.json | grep -o '[0-9.]*').x-dev" >> $GITHUB_ENV + echo "COMPOSER_ROOT_VERSION=$(grep branch-version composer.json | grep -o '[0-9.x]*').x-dev" >> $GITHUB_ENV - name: Determine composer cache directory id: composer-cache @@ -132,15 +156,19 @@ jobs: echo "::endgroup::" - name: Run tests - run: ./phpunit --group integration + run: ./phpunit --group integration -v env: REDIS_HOST: localhost REDIS_CLUSTER_HOSTS: 'localhost:7000 localhost:7001 localhost:7002 localhost:7003 localhost:7004 localhost:7005' + REDIS_SENTINEL_HOSTS: 'localhost:26379' + REDIS_SENTINEL_SERVICE: redis_sentinel MESSENGER_REDIS_DSN: redis://127.0.0.1:7006/messages MESSENGER_AMQP_DSN: amqp://localhost/%2f/messages MESSENGER_SQS_DSN: "sqs://localhost:9494/messages?sslmode=disable&poll_timeout=0.01" MESSENGER_SQS_FIFO_QUEUE_DSN: "sqs://localhost:9494/messages.fifo?sslmode=disable&poll_timeout=0.01" MEMCACHED_HOST: localhost + LDAP_HOST: localhost + LDAP_PORT: 3389 MONGODB_HOST: localhost KAFKA_BROKER: localhost:9092 diff --git a/.php_cs.dist b/.php_cs.dist index 0fd2436e791f7..4767e648853ad 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -15,6 +15,7 @@ return PhpCsFixer\Config::create() 'protected_to_private' => false, 'native_constant_invocation' => true, 'combine_nested_dirname' => true, + 'list_syntax' => ['syntax' => 'short'], ]) ->setRiskyAllowed(true) ->setFinder( diff --git a/.travis.yml b/.travis.yml index d1f90d9babcfe..e59103e3a27a1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,8 +9,6 @@ addons: apt_packages: - parallel - language-pack-fr-base - - ldap-utils - - slapd - zookeeperd - libzookeeper-mt-dev @@ -28,11 +26,11 @@ matrix: env: deps=high - php: 7.4 env: deps=low - - php: nightly + - php: 8.0snapshot services: [memcached] fast_finish: true allow_failures: - - php: nightly + - php: 8.0snapshot services: [memcached] cache: @@ -55,11 +53,6 @@ before_install: # General configuration set -e stty cols 120 - mkdir /tmp/slapd - if [ ! -e /tmp/slapd-modules ]; then - [ -d /usr/lib/openldap ] && ln -s /usr/lib/openldap /tmp/slapd-modules || ln -s /usr/lib/ldap /tmp/slapd-modules - fi - slapd -f src/Symfony/Component/Ldap/Tests/Fixtures/conf/slapd.conf -h ldap://localhost:3389 & cp .github/composer-config.json "$(composer config home)/config.json" export PHPUNIT=$(readlink -f ./phpunit) export PHPUNIT_X="$PHPUNIT --exclude-group tty,benchmark,intl-data" @@ -140,7 +133,7 @@ before_install: echo session.gc_probability = 0 >> $INI echo opcache.enable_cli = 1 >> $INI echo apc.enable_cli = 1 >> $INI - if [[ $PHP != nightly ]]; then + if [[ $PHP != 8.* ]]; then echo extension = memcached.so >> $INI fi done @@ -156,26 +149,19 @@ before_install: if ! php --ri sodium > /dev/null; then tfold ext.libsodium tpecl libsodium sodium.so $INI fi - if [[ $PHP = nightly ]]; then + if [[ $PHP = 8.* ]]; then tfold ext.memcached tpecl memcached-3.1.5 memcached.so $INI else tfold ext.mongodb tpecl mongodb-1.6.16 mongodb.so $INI tfold ext.zookeeper tpecl zookeeper-0.7.2 zookeeper.so $INI tfold ext.amqp tpecl amqp-1.10.2 amqp.so $INI - tfold ext.redis tpecl redis-5.2.2 redis.so $INI "no" fi tfold ext.apcu tpecl apcu-5.1.19 apcu.so $INI tfold ext.igbinary tpecl igbinary-3.1.6 igbinary.so $INI + tfold ext.redis tpecl redis-5.2.3 redis.so $INI "no" done - - | - # Load fixtures - if [[ ! $skip ]]; then - ldapadd -h localhost:3389 -D cn=admin,dc=symfony,dc=com -w symfony -f src/Symfony/Component/Ldap/Tests/Fixtures/data/base.ldif && - ldapadd -h localhost:3389 -D cn=admin,dc=symfony,dc=com -w symfony -f src/Symfony/Component/Ldap/Tests/Fixtures/data/fixtures.ldif - fi - install: - | # Install the phpunit-bridge from a PR if required @@ -199,7 +185,7 @@ install: git config --global user.email "" git config --global user.name "Symfony" - export SYMFONY_VERSION=$(grep branch-version composer.json | grep -o '[0-9.]*') + export SYMFONY_VERSION=$(grep branch-version composer.json | grep -o '[0-9.x]*') if [[ ! $deps ]]; then php .github/build-packages.php HEAD^ $SYMFONY_VERSION src/Symfony/Bridge/PhpUnit src/Symfony/Contracts @@ -234,7 +220,7 @@ install: - | # Set composer's platform to php 7.4 if we're on php 8. - if [[ $PHP = nightly ]]; then + if [[ $PHP = 8.* ]]; then composer config platform.php 7.4.99 export SYMFONY_DEPRECATIONS_HELPER=max[total]=999 fi diff --git a/CHANGELOG-5.1.md b/CHANGELOG-5.1.md index 0f8e3a5c324dd..3d530f59a41d1 100644 --- a/CHANGELOG-5.1.md +++ b/CHANGELOG-5.1.md @@ -7,6 +7,54 @@ in 5.1 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v5.1.0...v5.1.1 +* 5.1.9 (2020-11-29) + + * bug #39166 [Messenger] Fix mssql compatibility for doctrine transport. (bill moll) + * bug #39211 [HttpClient] fix binding to network interfaces (nicolas-grekas) + * bug #39129 [DependencyInjection] Fix circular in DI with lazy + byContruct loop (jderusse) + * bug #39068 [DependencyInjection][Translator] Silent deprecation triggered by libxml_disable_entity_loader (jderusse) + * bug #39119 [Form] prevent duplicated error message for file upload limits (xabbuh) + * bug #39099 [Form] ignore the pattern attribute for textareas (xabbuh) + * bug #39154 [Yaml] fix lexing strings containing escaped quotation characters (xabbuh) + * bug #39180 [Serializer] Fix denormalizing scalar with UnwrappingDenormalizer (camilledejoye) + * bug #38597 [PhpUnitBridge] Fix qualification of deprecations triggered by the debug class loader (fancyweb) + * bug #39160 [Console] Use a partial buffer in SymfonyStyle (jderusse) + * bug #39168 [Console] Fix console closing tag (jderusse) + * bug #39155 [VarDumper] fix casting resources turned into objects on PHP 8 (nicolas-grekas) + * bug #39131 [Cache] Fix CI because of Couchbase version (jderusse) + * bug #39115 [HttpClient] don't fallback to HTTP/1.1 when HTTP/2 streams break (nicolas-grekas) + * bug #33763 [Yaml] fix lexing nested sequences/mappings (xabbuh) + * bug #39083 [Dotenv] Check if method inheritEnvironmentVariables exists (Chi-teck) + * bug #39094 [Ldap] Fix undefined variable $con (derrabus) + * bug #39091 [Config] Recheck glob brace support after GlobResource was serialized (wouterj) + * bug #39092 Fix critical extension when reseting paged control (jderusse) + * bug #38614 [HttpFoundation] Fix for virtualhosts based on URL path (mvorisek) + * bug #39072 [FrameworkBundle] [Notifier] fix firebase transport factory DI tag type (xabbuh) + * bug #38387 [Validator] prevent hash collisions caused by reused object hashes (fancyweb, xabbuh) + * bug #38999 [DependencyInjection] autoconfigure behavior describing tags on decorators (xabbuh) + * bug #39058 [DependencyInjection] Fix circular detection with multiple paths (jderusse) + * bug #39059 [Filesystem] fix cleaning up tmp files when dumpFile() fails (nicolas-grekas) + * bug #38628 [DoctrineBridge] indexBy could reference to association columns (juanmiguelbesada) + * bug #39021 [DependencyInjection] Optimize circular collection by removing flattening (jderusse) + * bug #39031 [Ldap] Fix pagination (jderusse) + * bug #39038 [DoctrineBridge] also reset id readers (xabbuh) + * bug #39026 [Messenger] Fix DBAL deprecations in PostgreSqlConnection (chalasr) + * bug #39025 [DoctrineBridge] Fix DBAL deprecations in middlewares (derrabus) + * bug #38991 [Console] Fix ANSI when stdErr is not a tty (jderusse) + * bug #38980 [DependencyInjection] Fix circular reference with Factory + Lazy Iterrator (jderusse) + * bug #38977 [HttpClient] Check status code before decoding content in TraceableResponse (chalasr) + * bug #38971 [PhpUnitBridge] fix replaying skipped tests (nicolas-grekas) + * bug #38910 [HttpKernel] Fix session initialized several times (jderusse) + * bug #38882 [DependencyInjection] Improve performances in CircualReference detection (jderusse) + * bug #38950 [Process] Dont test TTY if there is no TTY support (Nyholm) + * bug #38921 [PHPUnitBridge] Fixed crash on Windows with PHP 8 (villfa) + * bug #38869 [SecurityBundle] inject only compatible token storage implementations for usage tracking (xabbuh) + * bug #38894 [HttpKernel] Remove Symfony 3 compatibility code (derrabus) + * bug #38895 [PhpUnitBridge] Fix wrong check for exporter in ConstraintTrait (alcaeus) + * bug #38879 [Cache] Fixed expiry could be int in ChainAdapter due to race conditions (phamviet) + * bug #38867 [FrameworkBundle] Fixing TranslationUpdateCommand failure when using "--no-backup" (liarco) + * bug #38856 [Cache] Add missing use statement (fabpot) + * 5.1.8 (2020-10-28) * bug #38713 [DI] Fix Preloader exception when preloading a class with an unknown parent/interface (rgeraads) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index f3217f25a966c..3bed011d06243 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -12,9 +12,10 @@ Symfony is the result of the work of many people who made the code better - Robin Chalas (chalas_r) - Christophe Coevoet (stof) - Kévin Dunglas (dunglas) - - Jordi Boggiano (seldaek) - Maxime Steinhausser (ogizanagi) + - Jordi Boggiano (seldaek) - Victor Berchet (victor) + - Alexander M. Turek (derrabus) - Grégoire Pineau (lyrixx) - Ryan Weaver (weaverryan) - Javier Eguiluz (javier.eguiluz) @@ -24,29 +25,29 @@ Symfony is the result of the work of many people who made the code better - Kris Wallsmith (kriswallsmith) - Wouter de Jong (wouterj) - Yonel Ceruto (yonelceruto) - - Alexander M. Turek (derrabus) - - Hugo Hamon (hhamon) - Thomas Calvet (fancyweb) + - Hugo Hamon (hhamon) + - Jérémy DERUSSÉ (jderusse) - Abdellatif Ait boudad (aitboudad) - Samuel ROZE (sroze) - Romain Neutron (romain) - Pascal Borreli (pborreli) - Joseph Bielawski (stloyd) - Karma Dordrak (drak) - - Jérémy DERUSSÉ (jderusse) - Jules Pietri (heah) - Lukas Kahwe Smith (lsmith) - Martin Hasoň (hason) - Hamza Amrouche (simperfit) + - Tobias Nyholm (tobias) - Jeremy Mikola (jmikola) - Jean-François Simon (jfsimon) - Benjamin Eberlei (beberlei) - Igor Wiedler (igorw) - Eriksen Costa (eriksencosta) - - Tobias Nyholm (tobias) - Guilhem Niot (energetick) - Sarah Khalil (saro0h) - Jonathan Wage (jwage) + - Jan Schädlich (jschaedl) - Lynn van der Berg (kjarli) - Matthias Pigulla (mpdude) - Diego Saint Esteben (dosten) @@ -55,28 +56,27 @@ Symfony is the result of the work of many people who made the code better - William Durand (couac) - Valentin Udaltsov (vudaltsov) - ornicar - - Jan Schädlich (jschaedl) + - Grégoire Paris (greg0ire) - Dany Maillard (maidmaid) - Francis Besset (francisbesset) - stealth35 ‏ (stealth35) - Alexander Mols (asm89) + - Kevin Bond (kbond) - Konstantin Myakshin (koc) - - Grégoire Paris (greg0ire) - Bulat Shakirzyanov (avalanche123) - - Kevin Bond (kbond) - Saša Stamenković (umpirsky) - Peter Rehm (rpet) - Gabriel Ostrolucký (gadelat) + - Titouan Galopin (tgalopin) - David Maicher (dmaicher) - Gábor Egyed (1ed) - Henrik Bjørnskov (henrikbjorn) - Miha Vrhovnik - - Titouan Galopin (tgalopin) - Diego Saint Esteben (dii3g0) - Konstantin Kudryashov (everzet) + - Mathieu Piot (mpiot) - Vladimir Reznichenko (kalessil) - Bilal Amarni (bamarni) - - Mathieu Piot (mpiot) - Florin Patan (florinpatan) - Jáchym Toušek (enumag) - Andrej Hudec (pulzarraider) @@ -86,13 +86,13 @@ Symfony is the result of the work of many people who made the code better - Charles Sarrazin (csarrazi) - Christian Raue - Douglas Greenshields (shieldo) + - Laurent VOULLEMIER (lvo) - Arnout Boks (aboks) + - Graham Campbell (graham) - Jérôme Tamarelle (gromnan) - - Laurent VOULLEMIER (lvo) - Deni - Henrik Westphal (snc) - Dariusz Górecki (canni) - - Graham Campbell (graham) - David Buchmann (dbu) - Dariusz Ruminski - Fran Moreno (franmomu) @@ -100,11 +100,11 @@ Symfony is the result of the work of many people who made the code better - Brandon Turner - Luis Cordova (cordoval) - Daniel Holmes (dholmes) + - Alex Pott - Toni Uebernickel (havvg) - Bart van den Burg (burgov) - Jordan Alliot (jalliot) - John Wards (johnwards) - - Alex Pott - Antoine Hérault (herzult) - Paráda József (paradajozsef) - Arnaud Le Blanc (arnaud-lb) @@ -116,21 +116,22 @@ Symfony is the result of the work of many people who made the code better - Tim Nagel (merk) - Chris Wilkinson (thewilkybarkid) - Brice BERNARD (brikou) + - Alexander Schranz (alexander-schranz) - marc.weistroff - Tomáš Votruba (tomas_votruba) - Peter Kokot (maastermedia) + - Lars Strojny (lstrojny) - lenar - Alexander Schwenn (xelaris) + - Massimiliano Arione (garak) - Włodzimierz Gajda (gajdaw) + - Oskar Stark (oskarstark) - Adrien Brault (adrienbrault) - - Lars Strojny (lstrojny) - - Massimiliano Arione (garak) - Jacob Dreesen (jdreesen) - Florian Voutzinos (florianv) - Teoh Han Hui (teohhanhui) - Przemysław Bogusz (przemyslaw-bogusz) - Colin Frei - - Oskar Stark (oskarstark) - Javier Spagnoletti (phansys) - Joshua Thijssen - Daniel Wehner (dawehner) @@ -138,12 +139,11 @@ Symfony is the result of the work of many people who made the code better - excelwebzone - Gordon Franke (gimler) - Joel Wurtz (brouznouf) - - Alexander Schranz (alexander-schranz) + - Antoine Makdessi (amakdessi) - Fabien Pennequin (fabienpennequin) - Julien Falque (julienfalque) - Théo FIDRY (theofidry) - Eric GELOEN (gelo) - - Antoine Makdessi (amakdessi) - Jannik Zschiesche (apfelbox) - jeremyFreeAgent (jeremyfreeagent) - Robert Schönthal (digitalkaoz) @@ -166,6 +166,7 @@ Symfony is the result of the work of many people who made the code better - Guilherme Blanco (guilhermeblanco) - SpacePossum - Pablo Godel (pgodel) + - Andreas Braun - Matthieu Napoli (mnapoli) - Richard van Laak (rvanlaak) - Jérémie Augustin (jaugustin) @@ -209,6 +210,7 @@ Symfony is the result of the work of many people who made the code better - Marek Štípek (maryo) - Filippo Tessarotto (slamdunk) - Daniel Espendiller + - Maxime Helias (maxhelias) - Possum - Dorian Villet (gnutix) - Michaël Perrin (michael.perrin) @@ -228,9 +230,9 @@ Symfony is the result of the work of many people who made the code better - Ruben Gonzalez (rubenrua) - Benjamin Dulau (dbenjamin) - Jan Rosier (rosier) - - Andreas Braun - Mathieu Lemoine (lemoinem) - Rémon van de Kamp (rpkamp) + - HypeMC - Christian Schmidt - Andreas Hucks (meandmymonkey) - Tom Van Looy (tvlooy) @@ -246,7 +248,6 @@ Symfony is the result of the work of many people who made the code better - Nikolay Labinskiy (e-moe) - Martin Schuhfuß (usefulthink) - apetitpa - - Maxime Helias (maxhelias) - Matthieu Bontemps (mbontemps) - apetitpa - Pierre Minnieur (pminnieur) @@ -255,6 +256,7 @@ Symfony is the result of the work of many people who made the code better - Dominique Bongiraud - Hidde Wieringa (hiddewie) - Jeremy Livingston (jeremylivingston) + - Olivier Dolbeau (odolbeau) - Michael Lee (zerustech) - Dmitrii Poddubnyi (karser) - Matthieu Auger (matthieuauger) @@ -288,13 +290,14 @@ Symfony is the result of the work of many people who made the code better - Marco Pivetta (ocramius) - Antonio Pauletich (x-coder264) - Jeroen Spee (jeroens) - - Olivier Dolbeau (odolbeau) - Rob Frawley 2nd (robfrawley) - julien pauli (jpauli) - Lorenz Schori - Sébastien Lavoie (lavoiesl) - Dariusz + - Farhad Safarov (safarov) - Michael Babker (mbabker) + - Thomas Lallement (raziel057) - Francois Zaninotto - Colin O'Dell (colinodell) - Alexander Kotynia (olden) @@ -304,22 +307,27 @@ Symfony is the result of the work of many people who made the code better - Elnur Abdurrakhimov (elnur) - Manuel Reinhard (sprain) - Danny Berger (dpb587) + - zairig imad (zairigimad) - Antonio J. García Lagar (ajgarlag) + - Alessandro Lai (jean85) - Adam Prager (padam87) - Benoît Burnichon (bburnichon) - Maciej Malarz (malarzm) - Roman Marintšenko (inori) - Xavier Montaña Carreras (xmontana) + - Timothée Barray (tyx) - Mickaël Andrieu (mickaelandrieu) - Xavier Perez - Arjen Brouwer (arjenjb) - Katsuhiro OGAWA - Patrick McDougle (patrick-mcdougle) + - Rokas Mikalkėnas (rokasm) - Marc Weistroff (futurecat) - Alif Rachmawadi - Anton Chernikov (anton_ch1989) - Kristen Gilden (kgilden) - Pierre-Yves LEBECQ (pylebecq) + - Benjamin Leveque (benji07) - Jordan Samouh (jordansamouh) - Jakub Kucharovic (jkucharovic) - Loick Piera (pyrech) @@ -335,16 +343,18 @@ Symfony is the result of the work of many people who made the code better - Ray - Chekote - Thomas Adam + - Chi-teck - Jhonny Lidfors (jhonne) - Diego Agulló (aeoris) - jdhoek + - Michael Käfer (michael_kaefer) - Bob den Otter (bopp) - Thomas Schulz (king2500) - Frank de Jonge (frenkynet) - Nikita Konstantinov - Wodor Wodorski + - Timo Bakx (timobakx) - Joe Bennett (kralos) - - Thomas Lallement (raziel057) - soyuka - Giorgio Premi - renanbr @@ -358,7 +368,6 @@ Symfony is the result of the work of many people who made the code better - Alexander Menshchikov (zmey_kk) - Emanuele Panzeri (thepanz) - Kim Hemsø Rasmussen (kimhemsoe) - - Alessandro Lai (jean85) - Langlet Vincent (deviling) - Pascal Luna (skalpa) - Wouter Van Hecke @@ -370,8 +379,9 @@ Symfony is the result of the work of many people who made the code better - Simon Mönch (sm) - Christian Schmidt - Patrick Landolt (scube) - - HypeMC - MatTheCat + - Denis Brumann (dbrumann) + - Bohan Yang (brentybh) - Vilius Grigaliūnas - David Badura (davidbadura) - Chad Sikorra (chadsikorra) @@ -379,7 +389,6 @@ Symfony is the result of the work of many people who made the code better - Chris Smith (cs278) - Thomas Bisignani (toma) - Florian Klein (docteurklein) - - Benjamin Leveque (benji07) - Manuel Kiessling (manuelkiessling) - Alexey Kopytko (sanmai) - Atsuhiro KUBO (iteman) @@ -399,7 +408,6 @@ Symfony is the result of the work of many people who made the code better - Emmanuel BORGES (eborges78) - Aurelijus Valeiša (aurelijus) - Jan Decavele (jandc) - - Chi-teck - Gustavo Piltcher - Jesse Rushlow (geeshoe) - Stepan Tanasiychuk (stfalcon) @@ -412,7 +420,6 @@ Symfony is the result of the work of many people who made the code better - Romain Pierre (romain-pierre) - Julien Galenski (ruian) - Thomas Landauer (thomas-landauer) - - Michael Käfer (michael_kaefer) - Bongiraud Dominique - janschoenherr - Emanuele Gaspari (inmarelibero) @@ -423,6 +430,8 @@ Symfony is the result of the work of many people who made the code better - Sebastien Morel (plopix) - Ricard Clau (ricardclau) - Mark Challoner (markchalloner) + - ivan + - Karoly Gossler (connorhu) - Ahmed Raafat - Philippe Segatori - Gennady Telegin (gtelegin) @@ -432,6 +441,7 @@ Symfony is the result of the work of many people who made the code better - Matthew Lewinski (lewinski) - Magnus Nordlander (magnusnordlander) - Thomas Royer (cydonia7) + - Nicolas Philippe (nikophil) - Nicolas LEFEVRE (nicoweb) - alquerci - Oleg Andreyev @@ -441,29 +451,33 @@ Symfony is the result of the work of many people who made the code better - Vitaliy Zakharov (zakharovvi) - Tobias Sjösten (tobiassjosten) - Gyula Sallai (salla) + - Romaric Drigon (romaricdrigon) - Inal DJAFAR (inalgnu) - Christian Gärtner (dagardner) - Dmytro Borysovskyi (dmytr0) - Tomasz Kowalczyk (thunderer) + - Sylvain Fabre (sylfabre) - Artur Eshenbrener - - Timo Bakx (timobakx) - Harm van Tilborg (hvt) - Thomas Perez (scullwm) - Felix Labrecque - Yaroslav Kiliba + - Ben Hakim - Terje Bråten - Gonzalo Vilaseca (gonzalovilaseca) + - Marco Petersen (ocrampete16) - Markus Fasselt (digilist) - Daniel STANCU - Robbert Klarenbeek (robbertkl) - Eric Masoero (eric-masoero) - Ion Bazan (ionbazan) - - Denis Brumann (dbrumann) + - Vitalii Ekert (comrade42) - JhonnyL - Clara van Miert - Haralan Dobrev (hkdobrev) - hossein zolfi (ocean) - Clément Gautier (clementgautier) + - Jeroen Noten (jeroennoten) - Bastien Jaillot (bastnic) - Dāvis Zālītis (k0d3r1s) - Sanpi @@ -477,7 +491,6 @@ Symfony is the result of the work of many people who made the code better - Dimitri Gritsajuk (ottaviano) - Kirill chEbba Chebunin (chebba) - - - Rokas Mikalkėnas (rokasm) - Greg Thornton (xdissent) - Alex Bowers - Philipp Cordes @@ -498,7 +511,6 @@ Symfony is the result of the work of many people who made the code better - Endre Fejes - Tobias Naumann (tna) - Daniel Beyer - - Timothée Barray (tyx) - Shein Alexey - Romain Gautier (mykiwi) - Joe Lencioni @@ -540,9 +552,11 @@ Symfony is the result of the work of many people who made the code better - Jeanmonod David (jeanmonod) - Christopher Davis (chrisguitarguy) - Webnet team (webnet) + - Ben Ramsey (ramsey) + - Nate Wiebe (natewiebe13) - Marcin Szepczynski (czepol) - Mohammad Emran Hasan (phpfour) - - Farhad Safarov + - Dmitriy Mamontov (mamontovdmitriy) - Jan Schumann - Niklas Fiekas - Markus Bachmann (baachi) @@ -554,6 +568,7 @@ Symfony is the result of the work of many people who made the code better - Mihai Stancu - Ivan Nikolaev (destillat) - Gildas Quéméner (gquemener) + - Baptiste Leduc (korbeil) - Laurent Masforné (heisenberg) - Claude Khedhiri (ck-developer) - Desjardins Jérôme (jewome62) @@ -563,22 +578,25 @@ Symfony is the result of the work of many people who made the code better - Toni Rudolf (toooni) - Asmir Mustafic (goetas) - DerManoMann - - Nicolas Philippe (nikophil) - vagrant - Aurimas Niekis (gcds) - EdgarPE + - Bob van de Vijver (bobvandevijver) - Florian Pfitzer (marmelatze) - Asier Illarramendi (doup) - - Sylvain Fabre (sylfabre) - Martijn Cuppens - Vlad Gregurco (vgregurco) - Boris Vujicic (boris.vujicic) - Artem Lopata - Judicaël RUFFIEUX (axanagor) - Chris Sedlmayr (catchamonkey) + - Indra Gunawan (indragunawan) - Kamil Kokot (pamil) - Seb Koelen - Christoph Mewes (xrstf) + - Andrew M-Y (andr) + - Krasimir Bosilkov (kbosilkov) + - Marcin Michalski (marcinmichalski) - Vitaliy Tverdokhlib (vitaliytv) - Ariel Ferrandini (aferrandini) - Dirk Pahl (dirkaholic) @@ -586,17 +604,20 @@ Symfony is the result of the work of many people who made the code better - Arkadius Stefanski (arkadius) - Tim Goudriaan (codedmonkey) - Jonas Flodén (flojon) + - AnneKir - Soner Sayakci - Tobias Weichart + - Miro Michalicka - Tarmo Leppänen (tarlepp) - Marcin Sikoń (marphi) - - Bohan Yang (brentybh) + - M. Vondano - Dominik Zogg (dominik.zogg) - Marek Pietrzak + - Tavo Nieves J - Luc Vieillescazes (iamluc) + - Lukáš Holeczy (holicz) - franek (franek) - Raulnet - - Marco Petersen (ocrampete16) - Christian Wahler - Giso Stallenberg (gisostallenberg) - Gintautas Miselis @@ -645,6 +666,7 @@ Symfony is the result of the work of many people who made the code better - Roy Van Ginneken (rvanginneken) - ondrowan - Barry vd. Heuvel (barryvdh) + - Michael Voříšek - Evan S Kaufman (evanskaufman) - mcben - Jérôme Vieilledent (lolautruche) @@ -659,6 +681,7 @@ Symfony is the result of the work of many people who made the code better - Leevi Graham (leevigraham) - Anthony Ferrara - Ioan Negulescu + - Greg ORIOL - Jakub Škvára (jskvara) - Andrew Udvare (audvare) - alexpods @@ -668,7 +691,6 @@ Symfony is the result of the work of many people who made the code better - Erik Trapman (eriktrapman) - De Cock Xavier (xdecock) - Almog Baku (almogbaku) - - Karoly Gossler (connorhu) - Scott Arciszewski - Xavier HAUSHERR - Norbert Orzechowicz (norzechowicz) @@ -735,22 +757,19 @@ Symfony is the result of the work of many people who made the code better - Julien Montel (julienmgel) - Mátyás Somfai (smatyas) - Bastien DURAND (deamon) - - Ben Ramsey (ramsey) - Simon DELICATA - Artem Henvald (artemgenvald) - Dmitry Simushev - alcaeus - Thomas Talbot (ioni) - - Nate Wiebe (natewiebe13) - Fred Cox - vitaliytv - - ivan - Philippe Segatori + - fd6130 (fdtvui) - Dalibor Karlović (dkarlovi) - Andrey Sevastianov - Sebastian Blum - Alexis Lefebvre - - Dmitriy Mamontov (mamontovdmitriy) - aubx - Julien Turby - Marvin Butkereit @@ -761,7 +780,6 @@ Symfony is the result of the work of many people who made the code better - Max Rath (drak3) - marie - Stéphane Escandell (sescandell) - - Baptiste Leduc (korbeil) - Konstantin S. M. Möllers (ksmmoellers) - James Johnston - Noémi Salaün (noemi-salaun) @@ -779,7 +797,6 @@ Symfony is the result of the work of many people who made the code better - Christophe Villeger (seragan) - Matthias Krauser (mkrauser) - Julien Fredon - - Bob van de Vijver (bobvandevijver) - Xavier Leune (xleune) - Stefan Gehrig (sgehrig) - Hany el-Kerdany @@ -794,9 +811,10 @@ Symfony is the result of the work of many people who made the code better - Jérôme Tamarelle (jtamarelle-prismamedia) - Geoffrey Brier (geoffrey-brier) - Alexandre Parent + - Roger Guasch (rogerguasch) - Vladimir Tsykun - - Romaric Drigon (romaricdrigon) - Dustin Dobervich (dustin10) + - Luis Tacón (lutacon) - dantleech - Philipp Kolesnikov - Anne-Sophie Bachelard (annesophie) @@ -804,7 +822,9 @@ Symfony is the result of the work of many people who made the code better - Carlos Pereira De Amorim (epitre) - zenmate - Michal Trojanowski + - Lescot Edouard (idetox) - David Fuhr + - Rodrigo Aguilera - Mathias STRASSER (roukmoute) - Max Grigorian (maxakawizard) - Rostyslav Kinash @@ -818,17 +838,18 @@ Symfony is the result of the work of many people who made the code better - Stefan Warman - Tristan Maindron (tmaindron) - Behnoush Norouzali (behnoush) + - Marko H. Tamminen (gzumba) - Wesley Lancel - Xavier Briand (xavierbriand) - Ke WANG (yktd26) - Ivo Bathke (ivoba) + - David Molineus - Strate - Anton A. Sumin - Israel J. Carberry - Miquel Rodríguez Telep (mrtorrent) - Sergey Kolodyazhnyy (skolodyazhnyy) - umpirski - - M. Vondano - Quentin de Longraye (quentinus95) - Chris Heng (gigablah) - Shaun Simmons (simshaun) @@ -843,8 +864,10 @@ Symfony is the result of the work of many people who made the code better - Michael Devery (mickadoo) - Antoine Corcy - Ahmed Ashraf (ahmedash95) + - Luca Saba (lucasaba) - Sascha Grossenbacher - Szijarto Tamas + - Thomas P - Robin Lehrmann (robinlehrmann) - Catalin Dan - Jaroslav Kuba @@ -908,7 +931,9 @@ Symfony is the result of the work of many people who made the code better - Peter Ward - Davide Borsatto (davide.borsatto) - Julien DIDIER (juliendidier) + - Randy Geraads - Dominik Ritter (dritter) + - Andreas Leathley (iquito) - Sebastian Grodzicki (sgrodzicki) - Mohamed Gamal - Jeroen van den Enden (stoefke) @@ -916,12 +941,12 @@ Symfony is the result of the work of many people who made the code better - Baldur Rensch (brensch) - Pierre Rineau - Fritz Michael Gschwantner - - Jeroen Noten (jeroennoten) - Vladyslav Petrovych - Alex Xandra Albert Sim - Carson Full - Sergey Yastrebov - Trent Steel (trsteel88) + - Steve Grunwell - Yuen-Chi Lian - Tarjei Huse (tarjei) - Besnik Br @@ -949,10 +974,10 @@ Symfony is the result of the work of many people who made the code better - Casper Valdemar Poulsen - Josiah (josiah) - Guillaume Verstraete (versgui) - - Greg ORIOL - Joschi Kuphal - John Bohn (jbohn) - Marc Morera (mmoreram) + - Jason Tan - BENOIT POLASZEK (bpolaszek) - Julien Pauli - Mathieu Rochette (mathroc) @@ -1026,7 +1051,6 @@ Symfony is the result of the work of many people who made the code better - Sergey Zolotov (enleur) - Maksim Kotlyar (makasim) - Neil Ferreira - - Indra Gunawan (indragunawan) - Julie Hourcade (juliehde) - Dmitry Parnas (parnas) - Paul LE CORRE @@ -1038,7 +1062,6 @@ Symfony is the result of the work of many people who made the code better - Gerard van Helden (drm) - flack (flack) - Johnny Peck (johnnypeck) - - Michael Voříšek - Stefan Kruppa - Ivan Menshykov - David Romaní @@ -1046,6 +1069,7 @@ Symfony is the result of the work of many people who made the code better - Gustavo Falco (gfalco) - Matt Robinson (inanimatt) - Kristof Van Cauwenbergh (kristofvc) + - Marco Lipparini (liarco) - Peter Bowyer (pbowyer) - Aleksey Podskrebyshev - Calin Mihai Pristavu @@ -1083,6 +1107,7 @@ Symfony is the result of the work of many people who made the code better - Don Pinkster - Maksim Muruev - Emil Einarsson + - Anderson Müller - 243083df - Thibault Duplessis - Rimas Kudelis @@ -1098,6 +1123,7 @@ Symfony is the result of the work of many people who made the code better - Johnson Page (jwpage) - Ruben Gonzalez (rubenruateltek) - Michael Roterman (wtfzdotnet) + - Dieter - Arno Geurts - Adán Lobato (adanlobato) - Ian Jenkins (jenkoian) @@ -1133,6 +1159,7 @@ Symfony is the result of the work of many people who made the code better - Erik Saunier (snickers) - Thiago Cordeiro (thiagocordeiro) - Rootie + - Bernd Stellwag - Alireza Mirsepassi (alirezamirsepassi) - Daniel Alejandro Castro Arellano (lexcast) - sensio @@ -1157,6 +1184,7 @@ Symfony is the result of the work of many people who made the code better - Christian Jul Jensen - Alexandre GESLIN (alexandregeslin) - The Whole Life to Learn + - Pierre Tondereau - Alex Vo (votanlean) - Mikkel Paulson - ergiegonzaga @@ -1180,7 +1208,9 @@ Symfony is the result of the work of many people who made the code better - Pieter - Michael Tibben - Hallison Boaventura (hallisonboaventura) + - Mas Iting - Billie Thompson + - Albion Bame (abame) - Ganesh Chandrasekaran - Sander Marechal - Franz Wilding (killerpoke) @@ -1201,10 +1231,15 @@ Symfony is the result of the work of many people who made the code better - Nicolas Martin (cocorambo) - Tom Panier (neemzy) - Fred Cox + - luffy1727 - Luciano Mammino (loige) - fabios - Sander Coolen (scoolen) + - Amirreza Shafaat (amirrezashafaat) + - Laurent Clouet + - Adoni Pavlakis (adoni) - Nicolas Le Goff (nlegoff) + - Ahmed EBEN HASSINE (famas23) - Ben Oman - Chris de Kok - Eduard Bulava (nonanerz) @@ -1213,21 +1248,24 @@ Symfony is the result of the work of many people who made the code better - Guillaume (guill) - Igor Timoshenko (igor.timoshenko) - Manuele Menozzi - - zairig imad (zairigimad) + - “teerasak” - Anton Babenko (antonbabenko) - Irmantas Šiupšinskas (irmantas) - Benoit Mallo - - Lescot Edouard (idetox) - Danilo Silva - Giuseppe Campanelli + - Valentin - pizzaminded - Arnaud PETITPAS (apetitpa) - Ken Stanley + - ivan - Zachary Tong (polyfractal) - linh + - Guilherme Augusto Henschel - Mario Blažek (marioblazek) - Ashura - Hryhorii Hrebiniuk + - Eric Krona - johnstevenson - hamza - dantleech @@ -1238,6 +1276,7 @@ Symfony is the result of the work of many people who made the code better - Stanislav Kocanda - DerManoMann - Damien Fayet (rainst0rm) + - Ippei SUmida (ippey_s) - MatTheCat - Guillaume Royer - Artem (digi) @@ -1247,6 +1286,7 @@ Symfony is the result of the work of many people who made the code better - Pierrick VIGNAND (pierrick) - Vadim Tyukov (vatson) - Arman + - Adamo Crespi (aerendir) - David Wolter (davewww) - Sortex - chispita @@ -1260,8 +1300,10 @@ Symfony is the result of the work of many people who made the code better - Danijel Obradović - Pablo Borowicz - Arjan Keeman + - Bruno Rodrigues de Araujo (brunosinister) - Máximo Cuadros (mcuadros) - Lukas Mencl + - Jacek Wilczyński (jacekwilczynski) - tamirvs - gauss - julien.galenski @@ -1322,12 +1364,14 @@ Symfony is the result of the work of many people who made the code better - Nikita Konstantinov - Martijn Evers - Philipp Fritsche + - tarlepp - Benjamin Paap (benjaminpaap) - Claus Due (namelesscoder) - Christian - Alexandru Patranescu - Denis Golubovskiy (bukashk0zzz) - Sergii Smertin (nfx) + - Quentin Moreau (sheitak) - Mikkel Paulson - Michał Strzelecki - hugofonseca (fonsecas72) @@ -1420,13 +1464,14 @@ Symfony is the result of the work of many people who made the code better - Arun Philip - Rémi Leclerc - Jan Vernarsky + - Jonas Hünig - Amine Yakoubi - Eduardo García Sanz (coma) - Sergio (deverad) - Makdessi Alex - James Gilliland - fduch (fduch) - - David Molineus + - Juan Miguel Besada Vidal (soutlink) - Stuart Fyfe - David de Boer (ddeboer) - Eno Mullaraj (emullaraj) @@ -1532,6 +1577,7 @@ Symfony is the result of the work of many people who made the code better - pthompson - Malaney J. Hill - Alexandre Pavy + - Adiel Cristo (arcristo) - Christian Flach (cmfcmf) - Cédric Girard (enk_) - Lars Ambrosius Wallenborn (larsborn) @@ -1547,6 +1593,7 @@ Symfony is the result of the work of many people who made the code better - Javier Espinosa - Anton Kroshilin - Dawid Sajdak + - Norman Soetbeer - Ludek Stepan - Aaron Stephens (astephens) - Craig Menning (cmenning) @@ -1557,7 +1604,6 @@ Symfony is the result of the work of many people who made the code better - Marc J. Schmidt (marcjs) - František Maša - Sebastian Schwarz - - Jason Tan - Marco Jantke - Saem Ghani - Clément LEFEBVRE @@ -1622,6 +1668,7 @@ Symfony is the result of the work of many people who made the code better - JL - Ilya Biryukov - Kim Laï Trinh + - Johan de Ruijter - Jason Desrosiers - m.chwedziak - Andreas Frömer @@ -1653,7 +1700,6 @@ Symfony is the result of the work of many people who made the code better - WedgeSama - Hugo Sales - Felds Liscia - - Randy Geraads - Chihiro Adachi (chihiro-adachi) - Raphaëll Roussel - Tadcka @@ -1669,7 +1715,6 @@ Symfony is the result of the work of many people who made the code better - Emmanuel Vella (emmanuel.vella) - Guillaume BRETOU (guiguiboy) - Carsten Nielsen (phreaknerd) - - Roger Guasch (rogerguasch) - Jay Severson - Benny Born - Emirald Mateli @@ -1694,6 +1739,7 @@ Symfony is the result of the work of many people who made the code better - Mara Blaga - Rick Prent - skalpa + - Kai - Martin Eckhardt - Bartłomiej Zając - Pieter Jordaan @@ -1702,6 +1748,8 @@ Symfony is the result of the work of many people who made the code better - Michael Dowling (mtdowling) - Karlos Presumido (oneko) - Tony Vermeiren (tony) + - Bart Wach + - Jos Elstgeest - Thomas Counsell - BilgeXA - r1pp3rj4ck @@ -1773,6 +1821,7 @@ Symfony is the result of the work of many people who made the code better - Flavian (2much) - Gautier Deuette - mike + - Gilbertsoft - tadas - Kirk Madera - Keith Maika @@ -1792,13 +1841,14 @@ Symfony is the result of the work of many people who made the code better - Zdeněk Drahoš - Dan Harper - moldcraft + - Marcin Kruk - Antoine Bellion (abellion) - Ramon Kleiss (akathos) - Antonio Peric-Mazar (antonioperic) - César Suárez (csuarez) - Bjorn Twachtmann (dotbjorn) + - Marek Víger (freezy) - Tobias Genberg (lorceroth) - - Luis Tacón (lutacon) - Nicolas Badey (nico-b) - Shane Preece (shane) - Johannes Goslar @@ -1853,6 +1903,7 @@ Symfony is the result of the work of many people who made the code better - Wing - Thomas Bibb - kick-the-bucket + - Joni Halme - Matt Farmer - catch - siganushka @@ -1966,6 +2017,7 @@ Symfony is the result of the work of many people who made the code better - Felix Marezki - Normunds - Luiz “Felds” Liscia + - Johan - Thomas Rothe - Adrien Wilmet - Martin @@ -2065,6 +2117,8 @@ Symfony is the result of the work of many people who made the code better - Ali Tavafi - Trevor Suarez - gedrox + - hugovms + - Viet Pham - Alan Bondarchuk - Pchol - dropfen @@ -2170,7 +2224,6 @@ Symfony is the result of the work of many people who made the code better - Marin Nicolae - Alessandro Loffredo - Ian Phillips - - Marco Lipparini - Haritz - Matthieu Prat - Grummfy @@ -2275,6 +2328,7 @@ Symfony is the result of the work of many people who made the code better - Vladimir Chernyshev (volch) - Wim Godden (wimg) - Yorkie Chadwick (yorkie76) + - Maxime Aknin (3m1x4m) - GuillaumeVerdon - Philipp Keck - Angel Fernando Quiroz Campos @@ -2378,12 +2432,13 @@ Symfony is the result of the work of many people who made the code better - Daniel Bannert - Karim Miladi - Michael Genereux + - Wojciech Kania - patrick-mcdougle - Dariusz Czech + - Bruno Baguette - Jack Wright - MrNicodemuz - Anonymous User - - Dieter - Paweł Tomulik - Eric J. Duran - Alexandru Bucur @@ -2399,7 +2454,6 @@ Symfony is the result of the work of many people who made the code better - n-aleha - Talha Zekeriya Durmuş - Anatol Belski - - Anderson Müller - Şəhriyar İmanov - Alexis BOYER - Kaipi Yann @@ -2454,6 +2508,7 @@ Symfony is the result of the work of many people who made the code better - Alex Nostadt - Michael Squires - Egor Gorbachev + - Fabien Villepinte - Derek Stephen McLean - Norman Soetbeer - zorn @@ -2546,6 +2601,7 @@ Symfony is the result of the work of many people who made the code better - Damián Nohales (eagleoneraptor) - Jordane VASPARD (elementaire) - Elliot Anderson (elliot) + - Erwan Nader (ernadoo) - Fabien D. (fabd) - Carsten Eilers (fnc) - Sorin Gitlan (forapathy) @@ -2619,6 +2675,7 @@ Symfony is the result of the work of many people who made the code better - Volker (skydiablo) - Success Go (successgo) - Julien Sanchez (sumbobyboys) + - Stephan Vierkant (svierkant) - Guillermo Gisinger (t3chn0r) - Markus Tacker (tacker) - Tom Newby (tomnewbyau) @@ -2645,6 +2702,7 @@ Symfony is the result of the work of many people who made the code better - simpson - Antoine Leblanc - drublic + - Andre Johnson - MaPePeR - Andreas Streichardt - Alexandre Segura diff --git a/composer.json b/composer.json index caf3a939c59d7..2fb8e26fb3bfa 100644 --- a/composer.json +++ b/composer.json @@ -114,7 +114,6 @@ "doctrine/data-fixtures": "^1.1", "doctrine/dbal": "~2.4|^3.0", "doctrine/orm": "~2.4,>=2.4.5", - "doctrine/reflection": "~1.0", "doctrine/doctrine-bundle": "^2.0", "guzzlehttp/promises": "^1.3.1", "masterminds/html5": "^2.6", diff --git a/phpunit b/phpunit index 71915eecb2b34..03bf63b0aae99 100755 --- a/phpunit +++ b/phpunit @@ -8,17 +8,12 @@ if (!file_exists(__DIR__.'/vendor/symfony/phpunit-bridge/bin/simple-phpunit')) { exit(1); } if (!getenv('SYMFONY_PHPUNIT_VERSION')) { - if (\PHP_VERSION_ID >= 70200) { - if (false === getenv('SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT') && false !== strpos(@file_get_contents(__DIR__.'/src/Symfony/Component/HttpKernel/Kernel.php'), 'const MAJOR_VERSION = 3;')) { - putenv('SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT=1'); - } - if (\PHP_VERSION_ID < 70300) { - putenv('SYMFONY_PHPUNIT_VERSION=8.5'); - } else { - putenv('SYMFONY_PHPUNIT_VERSION=9.4'); - } - } elseif (\PHP_VERSION_ID >= 70000) { - putenv('SYMFONY_PHPUNIT_VERSION=6.5'); + if (\PHP_VERSION_ID < 70200) { + putenv('SYMFONY_PHPUNIT_VERSION=7.5'); + } elseif (\PHP_VERSION_ID < 70300) { + putenv('SYMFONY_PHPUNIT_VERSION=8.5'); + } else { + putenv('SYMFONY_PHPUNIT_VERSION=9.4'); } } if (!getenv('SYMFONY_PATCH_TYPE_DECLARATIONS')) { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d2179994d502e..5bf27434011c1 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -15,7 +15,7 @@ - + diff --git a/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php b/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php index 67bf68da01596..8e292cf36b8d7 100644 --- a/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php +++ b/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php @@ -191,7 +191,7 @@ private function sanitizeQuery(string $connectionName, array $query): array } } - list($query['params'][$j], $explainable, $runnable) = $this->sanitizeParam($param, $e); + [$query['params'][$j], $explainable, $runnable] = $this->sanitizeParam($param, $e); if (!$explainable) { $query['explainable'] = false; } @@ -227,7 +227,7 @@ private function sanitizeParam($var, ?\Throwable $error): array $a = []; $explainable = $runnable = true; foreach ($var as $k => $v) { - list($value, $e, $r) = $this->sanitizeParam($v, null); + [$value, $e, $r] = $this->sanitizeParam($v, null); $explainable = $explainable && $e; $runnable = $runnable && $r; $a[$k] = $value; diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php index c1bbde27aa9c6..d2c8343ddb3c1 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php @@ -65,7 +65,7 @@ private function addTaggedSubscribers(ContainerBuilder $container) $taggedSubscribers = $this->findAndSortTags($subscriberTag, $container); foreach ($taggedSubscribers as $taggedSubscriber) { - list($id, $tag) = $taggedSubscriber; + [$id, $tag] = $taggedSubscriber; $connections = isset($tag['connection']) ? [$tag['connection']] : array_keys($this->connections); foreach ($connections as $con) { if (!isset($this->connections[$con])) { @@ -84,7 +84,7 @@ private function addTaggedListeners(ContainerBuilder $container) $listenerRefs = []; foreach ($taggedListeners as $taggedListener) { - list($id, $tag) = $taggedListener; + [$id, $tag] = $taggedListener; if (!isset($tag['event'])) { throw new InvalidArgumentException(sprintf('Doctrine event listener "%s" must specify the "event" attribute.', $id)); } diff --git a/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php b/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php index ca75617326331..f49eb7f73a642 100644 --- a/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php +++ b/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php @@ -49,7 +49,7 @@ public function guessType(string $class, string $property) return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\TextType', [], Guess::LOW_CONFIDENCE); } - list($metadata, $name) = $ret; + [$metadata, $name] = $ret; if ($metadata->hasAssociation($property)) { $multiple = $metadata->isCollectionValuedAssociation($property); diff --git a/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php b/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php index bccb1a9ebdc3e..21b78bada3019 100644 --- a/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php +++ b/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php @@ -244,6 +244,7 @@ public function getParent() public function reset() { + $this->idReaders = []; $this->entityLoaders = []; } diff --git a/src/Symfony/Bridge/Doctrine/Messenger/DoctrinePingConnectionMiddleware.php b/src/Symfony/Bridge/Doctrine/Messenger/DoctrinePingConnectionMiddleware.php index 3ec525d2cfd41..c6b219aa795ab 100644 --- a/src/Symfony/Bridge/Doctrine/Messenger/DoctrinePingConnectionMiddleware.php +++ b/src/Symfony/Bridge/Doctrine/Messenger/DoctrinePingConnectionMiddleware.php @@ -12,6 +12,7 @@ namespace Symfony\Bridge\Doctrine\Messenger; use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Middleware\StackInterface; @@ -38,8 +39,8 @@ private function pingConnection(EntityManagerInterface $entityManager) $connection = $entityManager->getConnection(); try { - $connection->query($connection->getDatabasePlatform()->getDummySelectSQL()); - } catch (DBALException $e) { + $connection->executeQuery($connection->getDatabasePlatform()->getDummySelectSQL()); + } catch (DBALException | Exception $e) { $connection->close(); $connection->connect(); } diff --git a/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php b/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php index c4e5b025c3014..732de95f9fd75 100644 --- a/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php +++ b/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php @@ -95,10 +95,24 @@ public function getTypes(string $class, string $property, array $context = []) $associationMapping = $metadata->getAssociationMapping($property); if (isset($associationMapping['indexBy'])) { - $indexColumn = $associationMapping['indexBy']; /** @var ClassMetadataInfo $subMetadata */ $subMetadata = $this->entityManager ? $this->entityManager->getClassMetadata($associationMapping['targetEntity']) : $this->classMetadataFactory->getMetadataFor($associationMapping['targetEntity']); - $typeOfField = $subMetadata->getTypeOfField($subMetadata->getFieldForColumn($indexColumn)); + + // Check if indexBy value is a property + $fieldName = $associationMapping['indexBy']; + if (null === ($typeOfField = $subMetadata->getTypeOfField($fieldName))) { + $fieldName = $subMetadata->getFieldForColumn($associationMapping['indexBy']); + //Not a property, maybe a column name? + if (null === ($typeOfField = $subMetadata->getTypeOfField($fieldName))) { + //Maybe the column name is the association join column? + $associationMapping = $subMetadata->getAssociationMapping($fieldName); + + /** @var ClassMetadataInfo $subMetadata */ + $indexProperty = $subMetadata->getSingleAssociationReferencedJoinColumnName($fieldName); + $subMetadata = $this->entityManager ? $this->entityManager->getClassMetadata($associationMapping['targetEntity']) : $this->classMetadataFactory->getMetadataFor($associationMapping['targetEntity']); + $typeOfField = $subMetadata->getTypeOfField($indexProperty); + } + } if (!$collectionKeyType = $this->getPhpType($typeOfField)) { return null; diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php index 89aa779301350..33e6a6fd87235 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php @@ -24,7 +24,7 @@ */ class EntityTypePerformanceTest extends FormPerformanceTestCase { - const ENTITY_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity'; + private const ENTITY_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity'; /** * @var \Doctrine\ORM\EntityManager diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php index ca9ab318d3f48..cc15da57ee6e8 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php @@ -39,14 +39,14 @@ class EntityTypeTest extends BaseTypeTest { const TESTED_TYPE = 'Symfony\Bridge\Doctrine\Form\Type\EntityType'; - const ITEM_GROUP_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\GroupableEntity'; - const SINGLE_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity'; - const SINGLE_IDENT_NO_TO_STRING_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdNoToStringEntity'; - const SINGLE_STRING_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringIdEntity'; - const SINGLE_ASSOC_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleAssociationToIntIdEntity'; - const SINGLE_STRING_CASTABLE_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringCastableIdEntity'; - const COMPOSITE_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIntIdEntity'; - const COMPOSITE_STRING_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeStringIdEntity'; + private const ITEM_GROUP_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\GroupableEntity'; + private const SINGLE_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity'; + private const SINGLE_IDENT_NO_TO_STRING_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdNoToStringEntity'; + private const SINGLE_STRING_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringIdEntity'; + private const SINGLE_ASSOC_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleAssociationToIntIdEntity'; + private const SINGLE_STRING_CASTABLE_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringCastableIdEntity'; + private const COMPOSITE_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIntIdEntity'; + private const COMPOSITE_STRING_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeStringIdEntity'; /** * @var EntityManager diff --git a/src/Symfony/Bridge/Doctrine/Tests/Messenger/DoctrinePingConnectionMiddlewareTest.php b/src/Symfony/Bridge/Doctrine/Tests/Messenger/DoctrinePingConnectionMiddlewareTest.php index e491558a2addf..f99a48527c442 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Messenger/DoctrinePingConnectionMiddlewareTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Messenger/DoctrinePingConnectionMiddlewareTest.php @@ -13,6 +13,7 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; use Doctrine\ORM\EntityManagerInterface; use Doctrine\Persistence\ManagerRegistry; use Symfony\Bridge\Doctrine\Messenger\DoctrinePingConnectionMiddleware; @@ -49,7 +50,7 @@ public function testMiddlewarePingOk() { $this->connection->expects($this->once()) ->method('getDatabasePlatform') - ->will($this->throwException(new DBALException())); + ->will($this->throwException(class_exists(Exception::class) ? new Exception() : new DBALException())); $this->connection->expects($this->once()) ->method('close') @@ -68,7 +69,7 @@ public function testMiddlewarePingResetEntityManager() { $this->connection->expects($this->once()) ->method('getDatabasePlatform') - ->will($this->throwException(new DBALException())); + ->will($this->throwException(class_exists(Exception::class) ? new Exception() : new DBALException())); $this->entityManager->expects($this->once()) ->method('isOpen') diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php index 4ea6a678e3eba..52d7a95f38576 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php @@ -68,10 +68,13 @@ public function testGetProperties() $expected = array_merge($expected, [ 'foo', 'bar', + 'indexedRguid', 'indexedBar', 'indexedFoo', + 'indexedBaz', 'indexedByDt', 'indexedByCustomType', + 'indexedBuz', ]); $this->assertEquals( @@ -147,6 +150,14 @@ public function typesProvider() new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineRelation') )]], + ['indexedRguid', [new Type( + Type::BUILTIN_TYPE_OBJECT, + false, + 'Doctrine\Common\Collections\Collection', + true, + new Type(Type::BUILTIN_TYPE_STRING), + new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineRelation') + )]], ['indexedBar', [new Type( Type::BUILTIN_TYPE_OBJECT, false, @@ -163,6 +174,14 @@ public function typesProvider() new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineRelation') )]], + ['indexedBaz', [new Type( + Type::BUILTIN_TYPE_OBJECT, + false, + Collection::class, + true, + new Type(Type::BUILTIN_TYPE_INT), + new Type(Type::BUILTIN_TYPE_OBJECT, false, DoctrineRelation::class) + )]], ['simpleArray', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))]], ['customFoo', null], ['notMapped', null], @@ -175,6 +194,14 @@ public function typesProvider() new Type(Type::BUILTIN_TYPE_OBJECT, false, DoctrineRelation::class) )]], ['indexedByCustomType', null], + ['indexedBuz', [new Type( + Type::BUILTIN_TYPE_OBJECT, + false, + Collection::class, + true, + new Type(Type::BUILTIN_TYPE_STRING), + new Type(Type::BUILTIN_TYPE_OBJECT, false, DoctrineRelation::class) + )]], ]; if (class_exists(Types::class)) { diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php index 568efce33d382..a065dc49cd2d4 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php @@ -41,6 +41,11 @@ class DoctrineDummy */ public $bar; + /** + * @ManyToMany(targetEntity="DoctrineRelation", indexBy="rguid") + */ + protected $indexedRguid; + /** * @ManyToMany(targetEntity="DoctrineRelation", indexBy="rguid_column") */ @@ -51,6 +56,11 @@ class DoctrineDummy */ protected $indexedFoo; + /** + * @OneToMany(targetEntity="DoctrineRelation", mappedBy="baz", indexBy="baz_id") + */ + protected $indexedBaz; + /** * @Column(type="guid") */ @@ -122,4 +132,9 @@ class DoctrineDummy * @OneToMany(targetEntity="DoctrineRelation", mappedBy="customType", indexBy="customType") */ private $indexedByCustomType; + + /** + * @OneToMany(targetEntity="DoctrineRelation", mappedBy="buzField", indexBy="buzField") + */ + protected $indexedBuz; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineFooType.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineFooType.php index e8f2b454cfa95..1131666483d22 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineFooType.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineFooType.php @@ -23,7 +23,7 @@ class DoctrineFooType extends Type /** * Type name. */ - const NAME = 'foo'; + private const NAME = 'foo'; /** * {@inheritdoc} diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php index e480ca9d777ba..d6d9af6d70c38 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php @@ -40,6 +40,11 @@ class DoctrineRelation */ protected $foo; + /** + * @ManyToOne(targetEntity="DoctrineDummy") + */ + protected $baz; + /** * @Column(type="datetime") */ @@ -49,4 +54,10 @@ class DoctrineRelation * @Column(type="foo") */ private $customType; + + /** + * @Column(type="guid", name="different_than_field") + * @ManyToOne(targetEntity="DoctrineDummy", inversedBy="indexedBuz") + */ + protected $buzField; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php index e9e905c89c3a4..1ccc4b7cc4feb 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php @@ -40,7 +40,7 @@ */ class UniqueEntityValidatorTest extends ConstraintValidatorTestCase { - const EM_NAME = 'foo'; + private const EM_NAME = 'foo'; /** * @var ObjectManager @@ -114,17 +114,8 @@ protected function createEntityManagerMock($repositoryMock) ->method('hasField') ->willReturn(true) ; - $reflParser = $this->getMockBuilder('Doctrine\Common\Reflection\StaticReflectionParser') - ->disableOriginalConstructor() - ->getMock() - ; - $refl = $this->getMockBuilder('Doctrine\Common\Reflection\StaticReflectionProperty') - ->setConstructorArgs([$reflParser, 'property-name']) - ->setMethods(['getValue']) - ->getMock() - ; + $refl = $this->createMock(\ReflectionProperty::class); $refl - ->expects($this->any()) ->method('getValue') ->willReturn(true) ; diff --git a/src/Symfony/Bridge/Doctrine/composer.json b/src/Symfony/Bridge/Doctrine/composer.json index b0189df45993e..2961eca001f58 100644 --- a/src/Symfony/Bridge/Doctrine/composer.json +++ b/src/Symfony/Bridge/Doctrine/composer.json @@ -47,8 +47,7 @@ "doctrine/collections": "~1.0", "doctrine/data-fixtures": "^1.1", "doctrine/dbal": "~2.4|^3.0", - "doctrine/orm": "^2.6.3", - "doctrine/reflection": "~1.0" + "doctrine/orm": "^2.6.3" }, "conflict": { "phpunit/phpunit": "<5.4.3", diff --git a/src/Symfony/Bridge/Monolog/Tests/LoggerTest.php b/src/Symfony/Bridge/Monolog/Tests/LoggerTest.php index f170c526b8f74..f3d2fd8f024bb 100644 --- a/src/Symfony/Bridge/Monolog/Tests/LoggerTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/LoggerTest.php @@ -75,7 +75,7 @@ public function testGetLogsWithDebugProcessor2() $logger->info('test'); $this->assertCount(1, $logger->getLogs()); - list($record) = $logger->getLogs(); + [$record] = $logger->getLogs(); $this->assertEquals('test', $record['message']); $this->assertEquals(Logger::INFO, $record['priority']); diff --git a/src/Symfony/Bridge/Monolog/Tests/Processor/WebProcessorTest.php b/src/Symfony/Bridge/Monolog/Tests/Processor/WebProcessorTest.php index 321bd7a8eed32..24aed730954e4 100644 --- a/src/Symfony/Bridge/Monolog/Tests/Processor/WebProcessorTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/Processor/WebProcessorTest.php @@ -22,7 +22,7 @@ class WebProcessorTest extends TestCase { public function testUsesRequestServerData() { - list($event, $server) = $this->createRequestEvent(); + [$event, $server] = $this->createRequestEvent(); $processor = new WebProcessor(); $processor->onKernelRequest($event); @@ -39,7 +39,7 @@ public function testUsesRequestServerData() public function testUseRequestClientIp() { Request::setTrustedProxies(['192.168.0.1'], Request::HEADER_X_FORWARDED_ALL); - list($event, $server) = $this->createRequestEvent(['X_FORWARDED_FOR' => '192.168.0.2']); + [$event, $server] = $this->createRequestEvent(['X_FORWARDED_FOR' => '192.168.0.2']); $processor = new WebProcessor(); $processor->onKernelRequest($event); @@ -61,7 +61,7 @@ public function testCanBeConstructedWithExtraFields() $this->markTestSkipped('WebProcessor of the installed Monolog version does not support $extraFields parameter'); } - list($event, $server) = $this->createRequestEvent(); + [$event, $server] = $this->createRequestEvent(); $processor = new WebProcessor(['url', 'referrer']); $processor->onKernelRequest($event); diff --git a/src/Symfony/Bridge/PhpUnit/.gitignore b/src/Symfony/Bridge/PhpUnit/.gitignore index c49a5d8df5c65..9d8c4aadaf9f5 100644 --- a/src/Symfony/Bridge/PhpUnit/.gitignore +++ b/src/Symfony/Bridge/PhpUnit/.gitignore @@ -1,3 +1,4 @@ vendor/ composer.lock phpunit.xml +Tests/DeprecationErrorHandler/fake_vendor/symfony/error-handler/DebugClassLoader.php diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php index 797ed145bb6b9..85d776638d3bb 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php @@ -13,6 +13,8 @@ use PHPUnit\Util\Test; use Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerFor; +use Symfony\Component\Debug\DebugClassLoader as LegacyDebugClassLoader; +use Symfony\Component\ErrorHandler\DebugClassLoader; /** * @internal @@ -58,6 +60,18 @@ public function __construct($message, array $trace, $file) } $this->trace = $trace; + + if ('trigger_error' === ($trace[1]['function'] ?? null) + && (DebugClassLoader::class === ($class = $trace[2]['class'] ?? null) || LegacyDebugClassLoader::class === $class) + && 'checkClass' === ($trace[2]['function'] ?? null) + && null !== ($extraFile = $trace[2]['args'][1] ?? null) + && '' !== $extraFile + && false !== $extraFile = realpath($extraFile) + ) { + $this->getOriginalFilesStack(); + array_splice($this->originalFilesStack, 2, 1, $extraFile); + } + $this->message = $message; $i = \count($this->trace); while (1 < $i && $this->lineShouldBeSkipped($this->trace[--$i])) { diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/ConstraintTraitForV7.php b/src/Symfony/Bridge/PhpUnit/Legacy/ConstraintTraitForV7.php index 1e625e463a1e8..b132f473c547e 100644 --- a/src/Symfony/Bridge/PhpUnit/Legacy/ConstraintTraitForV7.php +++ b/src/Symfony/Bridge/PhpUnit/Legacy/ConstraintTraitForV7.php @@ -45,7 +45,7 @@ protected function additionalFailureDescription($other): string protected function exporter(): Exporter { - if (null !== $this->exporter) { + if (null === $this->exporter) { $this->exporter = new Exporter(); } diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php index 693883ed78762..6238fc0281209 100644 --- a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php +++ b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php @@ -123,7 +123,7 @@ public function startTestSuite($suite) $suiteName = $suite->getName(); foreach ($suite->tests() as $test) { - if (!($test instanceof \PHPUnit\Framework\TestCase || $test instanceof TestCase)) { + if (!($test instanceof \PHPUnit_Framework_TestCase || $test instanceof TestCase)) { continue; } if (null === Test::getPreserveGlobalStateSettings(\get_class($test), $test->getName(false))) { @@ -158,7 +158,7 @@ public function startTestSuite($suite) $testSuites = [$suite]; for ($i = 0; isset($testSuites[$i]); ++$i) { foreach ($testSuites[$i]->tests() as $test) { - if ($test instanceof TestSuite) { + if ($test instanceof \PHPUnit_Framework_TestSuite || $test instanceof TestSuite) { if (!class_exists($test->getName(), false)) { $testSuites[] = $test; continue; @@ -174,12 +174,19 @@ public function startTestSuite($suite) } } } elseif (2 === $this->state) { + $suites = [$suite]; $skipped = []; - foreach ($suite->tests() as $test) { - if (!($test instanceof \PHPUnit\Framework\TestCase || $test instanceof TestCase) - || isset($this->wasSkipped[$suiteName]['*']) - || isset($this->wasSkipped[$suiteName][$test->getName()])) { - $skipped[] = $test; + while ($s = array_shift($suites)) { + foreach ($s->tests() as $test) { + if ($test instanceof \PHPUnit_Framework_TestSuite || $test instanceof TestSuite) { + $suites[] = $test; + continue; + } + if (($test instanceof \PHPUnit_Framework_TestCase || $test instanceof TestCase) + && isset($this->wasSkipped[\get_class($test)][$test->getName()]) + ) { + $skipped[] = $test; + } } } $suite->setTests($skipped); @@ -189,21 +196,13 @@ public function startTestSuite($suite) public function addSkippedTest($test, \Exception $e, $time) { if (0 < $this->state) { - if ($test instanceof \PHPUnit\Framework\TestCase || $test instanceof TestCase) { - $class = \get_class($test); - $method = $test->getName(); - } else { - $class = $test->getName(); - $method = '*'; - } - - $this->isSkipped[$class][$method] = 1; + $this->isSkipped[\get_class($test)][$test->getName()] = 1; } } public function startTest($test) { - if (-2 < $this->state && ($test instanceof \PHPUnit\Framework\TestCase || $test instanceof TestCase)) { + if (-2 < $this->state && ($test instanceof \PHPUnit_Framework_TestCase || $test instanceof TestCase)) { // This event is triggered before the test is re-run in isolation if ($this->willBeIsolated($test)) { $this->runsInSeparateProcess = tempnam(sys_get_temp_dir(), 'deprec'); @@ -313,7 +312,7 @@ public function endTest($test, $time) self::$expectedDeprecations = self::$gatheredDeprecations = []; self::$previousErrorHandler = null; } - if (!$this->runsInSeparateProcess && -2 < $this->state && ($test instanceof \PHPUnit\Framework\TestCase || $test instanceof TestCase)) { + if (!$this->runsInSeparateProcess && -2 < $this->state && ($test instanceof \PHPUnit_Framework_TestCase || $test instanceof TestCase)) { if (\in_array('time-sensitive', $groups, true)) { ClockMock::withClockMock(false); } diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/debug_class_loader_autoload.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/debug_class_loader_autoload.phpt new file mode 100644 index 0000000000000..781027e84fe66 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/debug_class_loader_autoload.phpt @@ -0,0 +1,51 @@ +--TEST-- +Test that a deprecation from the DebugClassLoader on a vendor class autoload triggered by an app class is considered indirect. +--FILE-- + +--EXPECTF-- +Remaining indirect deprecation notices (1) + + 1x: The "acme\lib\ExtendsDeprecatedClassFromOtherVendor" class extends "fcy\lib\DeprecatedClass" that is deprecated. + 1x in BarService::__construct from App\Services diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_app/BarService.php b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_app/BarService.php new file mode 100644 index 0000000000000..868de5bd443db --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_app/BarService.php @@ -0,0 +1,13 @@ + [__DIR__.'/../../fake_app/'], 'acme\\lib\\' => [__DIR__.'/../acme/lib/'], 'bar\\lib\\' => [__DIR__.'/../bar/lib/'], + 'fcy\\lib\\' => [__DIR__.'/../fcy/lib/'], ]; } public function loadClass($className) + { + if ($file = $this->findFile($className)) { + require $file; + } + } + + public function findFile($class) { foreach ($this->getPrefixesPsr4() as $prefix => $baseDirs) { - if (strpos($className, $prefix) !== 0) { + if (strpos($class, $prefix) !== 0) { continue; } foreach ($baseDirs as $baseDir) { - $file = str_replace([$prefix, '\\'], [$baseDir, '/'], $className.'.php'); + $file = str_replace([$prefix, '\\'], [$baseDir, '/'], $class.'.php'); if (file_exists($file)) { - require $file; + return $file; } } } + + return false; } } diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/fcy/lib/DeprecatedClass.php b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/fcy/lib/DeprecatedClass.php new file mode 100644 index 0000000000000..f6672cea20400 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/fcy/lib/DeprecatedClass.php @@ -0,0 +1,10 @@ +=5.5.9" }, "require-dev": { - "symfony/deprecation-contracts": "^2.1" + "symfony/deprecation-contracts": "^2.1", + "symfony/error-handler": "^4.4|^5.0" }, "suggest": { "symfony/error-handler": "For tracking deprecated interfaces usages at runtime with DebugClassLoader" diff --git a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php index a9772a2b87342..cd7642911ab87 100644 --- a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php +++ b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php @@ -11,9 +11,10 @@ namespace Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper; +use ProxyManager\Exception\ExceptionInterface; use ProxyManager\Generator\ClassGenerator; +use ProxyManager\Generator\MethodGenerator; use ProxyManager\GeneratorStrategy\BaseGeneratorStrategy; -use ProxyManager\Version; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface; @@ -83,7 +84,7 @@ public function getProxyCode(Definition $definition): string $code = $this->classGenerator->generate($this->generateProxyClass($definition)); $code = preg_replace('/^(class [^ ]++ extends )([^\\\\])/', '$1\\\\$2', $code); - if (version_compare(self::getProxyManagerVersion(), '2.2', '<')) { + if (!method_exists(MethodGenerator::class, 'fromReflectionWithoutBodyAndDocBlock')) { // proxy-manager < 2.2 $code = preg_replace( '/((?:\$(?:this|initializer|instance)->)?(?:publicProperties|initializer|valueHolder))[0-9a-f]++/', '${1}'.$this->getIdentifierSuffix($definition), @@ -91,22 +92,13 @@ public function getProxyCode(Definition $definition): string ); } - if (version_compare(self::getProxyManagerVersion(), '2.5', '<')) { + if (!is_subclass_of(ExceptionInterface::class, 'Throwable')) { // proxy-manager < 2.5 $code = preg_replace('/ \\\\Closure::bind\(function ((?:& )?\(\$instance(?:, \$value)?\))/', ' \Closure::bind(static function \1', $code); } return $code; } - private static function getProxyManagerVersion(): string - { - if (!class_exists(Version::class)) { - return '0.0.1'; - } - - return \defined(Version::class.'::VERSION') ? Version::VERSION : Version::getVersion(); - } - /** * Produces the proxy class name for the given definition. */ diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-implem.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-implem.php index 3362aab9fb5eb..684ff36581776 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-implem.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-implem.php @@ -76,7 +76,7 @@ public function & __get($name) $targetObject = $this->valueHolder%s; - $backtrace = debug_backtrace(false); + $backtrace = debug_backtrace(false%S); trigger_error( sprintf( 'Undefined property: %s::$%s in %s on line %s', @@ -115,8 +115,7 @@ public function __unset($name) $targetObject = $this->valueHolder%s; unset($targetObject->$name); -return; - } +%a } public function __clone() { diff --git a/src/Symfony/Bridge/Twig/Command/DebugCommand.php b/src/Symfony/Bridge/Twig/Command/DebugCommand.php index 974a38a9c3dca..8962a41dd366e 100644 --- a/src/Symfony/Bridge/Twig/Command/DebugCommand.php +++ b/src/Symfony/Bridge/Twig/Command/DebugCommand.php @@ -147,7 +147,7 @@ private function displayPathsText(SymfonyStyle $io, string $name) $shortnames[] = str_replace('\\', '/', $file->getRelativePathname()); } - list($namespace, $shortname) = $this->parseTemplateName($name); + [$namespace, $shortname] = $this->parseTemplateName($name); $alternatives = $this->findAlternatives($shortname, $shortnames); if (FilesystemLoader::MAIN_NAMESPACE !== $namespace) { $alternatives = array_map(function ($shortname) use ($namespace) { @@ -452,7 +452,7 @@ private function error(SymfonyStyle $io, string $message, array $alternatives = private function findTemplateFiles(string $name): array { - list($namespace, $shortname) = $this->parseTemplateName($name); + [$namespace, $shortname] = $this->parseTemplateName($name); $files = []; foreach ($this->getFilesystemLoaders() as $loader) { diff --git a/src/Symfony/Bridge/Twig/Extension/CodeExtension.php b/src/Symfony/Bridge/Twig/Extension/CodeExtension.php index 0ff70eccd6886..7acf75fb9c17a 100644 --- a/src/Symfony/Bridge/Twig/Extension/CodeExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/CodeExtension.php @@ -66,7 +66,7 @@ public function abbrClass(string $class): string public function abbrMethod(string $method): string { if (false !== strpos($method, '::')) { - list($class, $method) = explode('::', $method, 2); + [$class, $method] = explode('::', $method, 2); $result = sprintf('%s::%s()', $this->abbrClass($class), $method); } elseif ('Closure' === $method) { $result = sprintf('%1$s', $method); diff --git a/src/Symfony/Bridge/Twig/Node/TransNode.php b/src/Symfony/Bridge/Twig/Node/TransNode.php index da2b85d1c2861..8a126ba569172 100644 --- a/src/Symfony/Bridge/Twig/Node/TransNode.php +++ b/src/Symfony/Bridge/Twig/Node/TransNode.php @@ -52,7 +52,7 @@ public function compile(Compiler $compiler): void $defaults = $this->getNode('vars'); $vars = null; } - list($msg, $defaults) = $this->compileString($this->getNode('body'), $defaults, (bool) $vars); + [$msg, $defaults] = $this->compileString($this->getNode('body'), $defaults, (bool) $vars); $compiler ->write('echo $this->env->getExtension(\'Symfony\Bridge\Twig\Extension\TranslationExtension\')->trans(') diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTest.php index 0a9319472552b..73ec1c18277e2 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTest.php @@ -2393,7 +2393,7 @@ public function testTextarea() $this->assertWidgetMatchesXpath($form->createView(), ['attr' => ['class' => 'my&class']], '/textarea [@name="name"] - [@pattern="foo"] + [not(@pattern)] [@class="my&class form-control"] [.="foo&bar"] ' diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index 5454e868e0766..877b7e1c01aad 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -26,7 +26,7 @@ "symfony/asset": "^4.4|^5.0", "symfony/dependency-injection": "^4.4|^5.0", "symfony/finder": "^4.4|^5.0", - "symfony/form": "^5.1", + "symfony/form": "^5.1.9", "symfony/http-foundation": "^4.4|^5.0", "symfony/http-kernel": "^4.4|^5.0", "symfony/mime": "^4.4|^5.0", diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index 9815c8fd12616..1b5fc917beb54 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -3,7 +3,7 @@ CHANGELOG 5.1.0 ----- - + * Removed `--no-backup` option from `translation:update` command (broken since `5.0.0`) * Added link to source for controllers registered as named services * Added link to source on controller on `router:match`/`debug:router` (when `framework.ide` is configured) * Added the `framework.router.default_uri` configuration option to configure the default `RequestContext` diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php index 57a9a19157fa3..f43e236b7a402 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php @@ -79,7 +79,6 @@ protected function configure() new InputOption('output-format', null, InputOption::VALUE_OPTIONAL, 'Override the default output format', 'xlf'), new InputOption('dump-messages', null, InputOption::VALUE_NONE, 'Should the messages be dumped in the console'), new InputOption('force', null, InputOption::VALUE_NONE, 'Should the update be done'), - new InputOption('no-backup', null, InputOption::VALUE_NONE, 'Should backup be disabled'), new InputOption('clean', null, InputOption::VALUE_NONE, 'Should clean not found messages'), new InputOption('domain', null, InputOption::VALUE_OPTIONAL, 'Specify the domain to update'), new InputOption('xliff-version', null, InputOption::VALUE_OPTIONAL, 'Override the default xliff version', '1.2'), @@ -283,10 +282,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int $resultMessage = sprintf('%d message%s successfully extracted', $extractedMessagesCount, $extractedMessagesCount > 1 ? 's were' : ' was'); } - if (true === $input->getOption('no-backup')) { - $this->writer->disableBackup(); - } - // save the files if (true === $input->getOption('force')) { $io->comment('Writing files...'); diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 60302152b10e7..5b3c3d541cc57 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -493,6 +493,14 @@ public function load(array $configs, ContainerBuilder $container) $container->registerForAutoconfiguration(RouteLoaderInterface::class) ->addTag('routing.route_loader'); + + $container->setParameter('container.behavior_describing_tags', [ + 'container.service_locator', + 'container.service_subscriber', + 'kernel.event_subscriber', + 'kernel.locale_aware', + 'kernel.reset', + ]); } /** @@ -609,7 +617,7 @@ private function registerProfilerConfiguration(array $config, ContainerBuilder $ $container->setParameter('profiler_listener.only_master_requests', $config['only_master_requests']); // Choose storage class based on the DSN - list($class) = explode(':', $config['dsn'], 2); + [$class] = explode(':', $config['dsn'], 2); if ('file' !== $class) { throw new \LogicException(sprintf('Driver "%s" is not supported for the profiler.', $class)); } diff --git a/src/Symfony/Bundle/FrameworkBundle/KernelBrowser.php b/src/Symfony/Bundle/FrameworkBundle/KernelBrowser.php index 6e44657cf8181..40381e34aa310 100644 --- a/src/Symfony/Bundle/FrameworkBundle/KernelBrowser.php +++ b/src/Symfony/Bundle/FrameworkBundle/KernelBrowser.php @@ -65,7 +65,7 @@ public function getKernel() /** * Gets the profile associated with the current Response. * - * @return HttpProfile|false A Profile instance + * @return HttpProfile|false|null A Profile instance */ public function getProfile() { diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/mime_type.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/mime_type.xml index d4c1eb15b9088..e91705d1c19ed 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/mime_type.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/mime_type.xml @@ -12,5 +12,7 @@ + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.xml index 045eb52a1b96e..42a4f5183f618 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.xml @@ -35,7 +35,7 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index c6f158eb8c0d0..52b4f481163bd 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -1477,6 +1477,20 @@ public function testMailerWithSpecificMessageBus(): void $this->assertEquals(new Reference('app.another_bus'), $container->getDefinition('mailer.mailer')->getArgument(1)); } + public function testRegisterParameterCollectingBehaviorDescribingTags() + { + $container = $this->createContainerFromFile('default_config'); + + $this->assertTrue($container->hasParameter('container.behavior_describing_tags')); + $this->assertEquals([ + 'container.service_locator', + 'container.service_subscriber', + 'kernel.event_subscriber', + 'kernel.locale_aware', + 'kernel.reset', + ], $container->getParameter('container.behavior_describing_tags')); + } + protected function createContainer(array $data = []) { return new ContainerBuilder(new EnvPlaceholderParameterBag(array_merge([ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SessionController.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SessionController.php index 0d9464d7dfab4..168cd2d45a52a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SessionController.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SessionController.php @@ -71,7 +71,7 @@ public function showFlashAction(Request $request) $session = $request->getSession(); if ($session->getFlashBag()->has('notice')) { - list($output) = $session->getFlashBag()->get('notice'); + [$output] = $session->getFlashBag()->get('notice'); } else { $output = 'No flash was set.'; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php b/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php index 8ae38fd392168..08c920fd951c6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php +++ b/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php @@ -155,7 +155,7 @@ protected function initialize() $this->addResourceFiles(); } foreach ($this->resources as $key => $params) { - list($format, $resource, $locale, $domain) = $params; + [$format, $resource, $locale, $domain] = $params; parent::addResource($format, $resource, $locale, $domain); } $this->resources = []; diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterTokenUsageTrackingPass.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterTokenUsageTrackingPass.php index 94d5a184727e8..1cd90fe70af1a 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterTokenUsageTrackingPass.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterTokenUsageTrackingPass.php @@ -45,8 +45,12 @@ public function process(ContainerBuilder $container) $container->setAlias('security.token_storage', 'security.untracked_token_storage')->setPublic(true); $container->getDefinition('security.untracked_token_storage')->addTag('kernel.reset', ['method' => 'reset']); } elseif ($container->hasDefinition('security.context_listener')) { - $container->getDefinition('security.context_listener') - ->setArgument(6, [new Reference('security.token_storage'), 'enableUsageTracking']); + $tokenStorageClass = $container->getParameterBag()->resolveValue($container->findDefinition('security.token_storage')->getClass()); + + if (method_exists($tokenStorageClass, 'enableUsageTracking')) { + $container->getDefinition('security.context_listener') + ->setArgument(6, [new Reference('security.token_storage'), 'enableUsageTracking']); + } } } } diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 7202b2e5a95e0..a1a972cb32d26 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -271,7 +271,7 @@ private function createFirewalls(array $config, ContainerBuilder $container) $configId = 'security.firewall.map.config.'.$name; - list($matcher, $listeners, $exceptionListener, $logoutListener) = $this->createFirewall($container, $name, $firewall, $authenticationProviders, $providerIds, $configId); + [$matcher, $listeners, $exceptionListener, $logoutListener] = $this->createFirewall($container, $name, $firewall, $authenticationProviders, $providerIds, $configId); $contextId = 'security.firewall.map.context.'.$name; $isLazy = !$firewall['stateless'] && (!empty($firewall['anonymous']['lazy']) || $firewall['lazy']); @@ -449,7 +449,7 @@ private function createFirewall(ContainerBuilder $container, string $id, array $ // Authentication listeners $firewallAuthenticationProviders = []; - list($authListeners, $defaultEntryPoint) = $this->createAuthenticationListeners($container, $id, $firewall, $firewallAuthenticationProviders, $defaultProvider, $providerIds, $configuredEntryPoint, $contextListenerId); + [$authListeners, $defaultEntryPoint] = $this->createAuthenticationListeners($container, $id, $firewall, $firewallAuthenticationProviders, $defaultProvider, $providerIds, $configuredEntryPoint, $contextListenerId); if (!$this->authenticatorManagerEnabled) { $authenticationProviders = array_merge($authenticationProviders, $firewallAuthenticationProviders); @@ -567,7 +567,7 @@ private function createAuthenticationListeners(ContainerBuilder $container, stri $entryPoints[$key] = $entryPoint; } } else { - list($provider, $listenerId, $defaultEntryPoint) = $factory->create($container, $id, $firewall[$key], $userProvider, $defaultEntryPoint); + [$provider, $listenerId, $defaultEntryPoint] = $factory->create($container, $id, $firewall[$key], $userProvider, $defaultEntryPoint); $listeners[] = new Reference($listenerId); $authenticationProviders[] = $provider; diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/RegisterTokenUsageTrackingPassTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/RegisterTokenUsageTrackingPassTest.php new file mode 100644 index 0000000000000..afdbf9afaf60f --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/RegisterTokenUsageTrackingPassTest.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\RegisterTokenUsageTrackingPass; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; +use Symfony\Component\Security\Core\Authentication\Token\Storage\UsageTrackingTokenStorage; +use Symfony\Component\Security\Http\Firewall\ContextListener; + +class RegisterTokenUsageTrackingPassTest extends TestCase +{ + public function testTokenStorageIsUntrackedIfSessionIsMissing() + { + $container = new ContainerBuilder(); + $container->register('security.untracked_token_storage', TokenStorage::class); + + $compilerPass = new RegisterTokenUsageTrackingPass(); + $compilerPass->process($container); + + $this->assertTrue($container->hasAlias('security.token_storage')); + $this->assertEquals(new Alias('security.untracked_token_storage', true), $container->getAlias('security.token_storage')); + } + + public function testContextListenerIsNotModifiedIfTokenStorageDoesNotSupportUsageTracking() + { + $container = new ContainerBuilder(); + + $container->setParameter('security.token_storage.class', TokenStorage::class); + $container->register('session', Session::class); + $container->register('security.context_listener', ContextListener::class) + ->setArguments([ + new Reference('security.untracked_token_storage'), + [], + 'main', + new Reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE), + new Reference('event_dispatcher', ContainerInterface::NULL_ON_INVALID_REFERENCE), + new Reference('security.authentication.trust_resolver'), + ]); + $container->register('security.token_storage', '%security.token_storage.class%'); + $container->register('security.untracked_token_storage', TokenStorage::class); + + $compilerPass = new RegisterTokenUsageTrackingPass(); + $compilerPass->process($container); + + $this->assertCount(6, $container->getDefinition('security.context_listener')->getArguments()); + } + + public function testContextListenerEnablesUsageTrackingIfSupportedByTokenStorage() + { + $container = new ContainerBuilder(); + + $container->setParameter('security.token_storage.class', UsageTrackingTokenStorage::class); + $container->register('session', Session::class); + $container->register('security.context_listener', ContextListener::class) + ->setArguments([ + new Reference('security.untracked_token_storage'), + [], + 'main', + new Reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE), + new Reference('event_dispatcher', ContainerInterface::NULL_ON_INVALID_REFERENCE), + new Reference('security.authentication.trust_resolver'), + ]); + $container->register('security.token_storage', '%security.token_storage.class%'); + $container->register('security.untracked_token_storage', TokenStorage::class); + + $compilerPass = new RegisterTokenUsageTrackingPass(); + $compilerPass->process($container); + + $contextListener = $container->getDefinition('security.context_listener'); + + $this->assertCount(7, $container->getDefinition('security.context_listener')->getArguments()); + $this->assertEquals([new Reference('security.token_storage'), 'enableUsageTracking'], $contextListener->getArgument(6)); + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php index a7f56e9958785..f9d8094fbccd4 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php @@ -235,7 +235,7 @@ public function testAccess() } $matcherIds = []; - foreach ($rules as list($matcherId, $attributes, $channel)) { + foreach ($rules as [$matcherId, $attributes, $channel]) { $requestMatcher = $container->getDefinition($matcherId); $this->assertArrayNotHasKey($matcherId, $matcherIds); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php index 01e03b0312bd9..3360863159a90 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php @@ -19,7 +19,7 @@ class AbstractFactoryTest extends TestCase { public function testCreate() { - list($container, $authProviderId, $listenerId, $entryPointId) = $this->callFactory('foo', [ + [$container, $authProviderId, $listenerId, $entryPointId] = $this->callFactory('foo', [ 'use_forward' => true, 'failure_path' => '/foo', 'success_handler' => 'custom_success_handler', @@ -61,7 +61,7 @@ public function testDefaultFailureHandler($serviceId, $defaultHandlerInjection) $options['failure_handler'] = $serviceId; } - list($container) = $this->callFactory('foo', $options, 'user_provider', 'entry_point'); + [$container] = $this->callFactory('foo', $options, 'user_provider', 'entry_point'); $definition = $container->getDefinition('abstract_listener.foo'); $arguments = $definition->getArguments(); @@ -99,7 +99,7 @@ public function testDefaultSuccessHandler($serviceId, $defaultHandlerInjection) $options['success_handler'] = $serviceId; } - list($container) = $this->callFactory('foo', $options, 'user_provider', 'entry_point'); + [$container] = $this->callFactory('foo', $options, 'user_provider', 'entry_point'); $definition = $container->getDefinition('abstract_listener.foo'); $arguments = $definition->getArguments(); @@ -150,7 +150,7 @@ protected function callFactory($id, $config, $userProviderId, $defaultEntryPoint $container->register('custom_success_handler'); $container->register('custom_failure_handler'); - list($authProviderId, $listenerId, $entryPointId) = $factory->create($container, $id, $config, $userProviderId, $defaultEntryPointId); + [$authProviderId, $listenerId, $entryPointId] = $factory->create($container, $id, $config, $userProviderId, $defaultEntryPointId); return [$container, $authProviderId, $listenerId, $entryPointId]; } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/GuardAuthenticationFactoryTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/GuardAuthenticationFactoryTest.php index 8215aaf9c90fb..7d6f5f6591278 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/GuardAuthenticationFactoryTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/GuardAuthenticationFactoryTest.php @@ -104,7 +104,7 @@ public function testBasicCreate() 'authenticators' => ['authenticator123'], 'entry_point' => null, ]; - list($container, $entryPointId) = $this->executeCreate($config, null); + [$container, $entryPointId] = $this->executeCreate($config, null); $this->assertEquals('authenticator123', $entryPointId); $providerDefinition = $container->getDefinition('security.authentication.provider.guard.my_firewall'); @@ -127,7 +127,7 @@ public function testExistingDefaultEntryPointUsed() 'authenticators' => ['authenticator123'], 'entry_point' => null, ]; - list(, $entryPointId) = $this->executeCreate($config, 'some_default_entry_point'); + [, $entryPointId] = $this->executeCreate($config, 'some_default_entry_point'); $this->assertEquals('some_default_entry_point', $entryPointId); } @@ -160,7 +160,7 @@ public function testCreateWithEntryPoint() 'authenticators' => ['authenticator123', 'authenticatorABC'], 'entry_point' => 'authenticatorABC', ]; - list(, $entryPointId) = $this->executeCreate($config, null); + [, $entryPointId] = $this->executeCreate($config, null); $this->assertEquals('authenticatorABC', $entryPointId); } @@ -196,7 +196,7 @@ private function executeCreate(array $config, $defaultEntryPointId) $userProviderId = 'my_user_provider'; $factory = new GuardAuthenticationFactory(); - list(, , $entryPointId) = $factory->create($container, $id, $config, $userProviderId, $defaultEntryPointId); + [, , $entryPointId] = $factory->create($container, $id, $config, $userProviderId, $defaultEntryPointId); return [$container, $entryPointId]; } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallMapTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallMapTest.php index fa590a54e908d..7e49cd4fde9b5 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallMapTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallMapTest.php @@ -23,7 +23,7 @@ class FirewallMapTest extends TestCase { - const ATTRIBUTE_FIREWALL_CONTEXT = '_firewall_context'; + private const ATTRIBUTE_FIREWALL_CONTEXT = '_firewall_context'; public function testGetListenersWithEmptyMap() { diff --git a/src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.php b/src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.php index 30dd95e5c75eb..bb9daab006fce 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.php @@ -72,7 +72,7 @@ public function getNames(Profile $profile) continue; } - list($name, $template) = $arguments; + [$name, $template] = $arguments; if (!$this->profiler->has($name) || !$profile->hasCollector($name)) { continue; diff --git a/src/Symfony/Component/BrowserKit/Cookie.php b/src/Symfony/Component/BrowserKit/Cookie.php index 79ca7943109ff..c24661de76ab5 100644 --- a/src/Symfony/Component/BrowserKit/Cookie.php +++ b/src/Symfony/Component/BrowserKit/Cookie.php @@ -133,7 +133,7 @@ public static function fromString(string $cookie, string $url = null) throw new \InvalidArgumentException(sprintf('The cookie string "%s" is not valid.', $parts[0])); } - list($name, $value) = explode('=', array_shift($parts), 2); + [$name, $value] = explode('=', array_shift($parts), 2); $values = [ 'name' => trim($name), diff --git a/src/Symfony/Component/Cache/Adapter/ChainAdapter.php b/src/Symfony/Component/Cache/Adapter/ChainAdapter.php index efca364ba7ce6..aef2a9a7deb57 100644 --- a/src/Symfony/Component/Cache/Adapter/ChainAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/ChainAdapter.php @@ -74,7 +74,7 @@ static function ($sourceItem, $item, $sourceMetadata = null) use ($defaultLifeti $item->metadata = $item->newMetadata = $sourceItem->metadata = $sourceMetadata; if (isset($item->metadata[CacheItem::METADATA_EXPIRY])) { - $item->expiresAt(\DateTime::createFromFormat('U.u', $item->metadata[CacheItem::METADATA_EXPIRY])); + $item->expiresAt(\DateTime::createFromFormat('U.u', sprintf('%.6F', $item->metadata[CacheItem::METADATA_EXPIRY]))); } elseif (0 < $defaultLifetime) { $item->expiresAfter($defaultLifetime); } diff --git a/src/Symfony/Component/Cache/Adapter/CouchbaseBucketAdapter.php b/src/Symfony/Component/Cache/Adapter/CouchbaseBucketAdapter.php index 0fd272700198c..36667f2a0dfb9 100644 --- a/src/Symfony/Component/Cache/Adapter/CouchbaseBucketAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/CouchbaseBucketAdapter.php @@ -42,7 +42,7 @@ class CouchbaseBucketAdapter extends AbstractAdapter public function __construct(\CouchbaseBucket $bucket, string $namespace = '', int $defaultLifetime = 0, MarshallerInterface $marshaller = null) { if (!static::isSupported()) { - throw new CacheException('Couchbase >= 2.6.0 is required.'); + throw new CacheException('Couchbase >= 2.6.0 < 3.0.0 is required.'); } $this->maxIdLength = static::MAX_KEY_LENGTH; @@ -66,7 +66,7 @@ public static function createConnection($servers, array $options = []): \Couchba } if (!static::isSupported()) { - throw new CacheException('Couchbase >= 2.6.0 is required.'); + throw new CacheException('Couchbase >= 2.6.0 < 3.0.0 is required.'); } set_error_handler(function ($type, $msg, $file, $line) { throw new \ErrorException($msg, 0, $type, $file, $line); }); @@ -125,7 +125,7 @@ public static function createConnection($servers, array $options = []): \Couchba public static function isSupported(): bool { - return \extension_loaded('couchbase') && version_compare(phpversion('couchbase'), '2.6.0', '>='); + return \extension_loaded('couchbase') && version_compare(phpversion('couchbase'), '2.6.0', '>=') && version_compare(phpversion('couchbase'), '3.0', '<'); } private static function getOptions(string $options): array @@ -134,7 +134,7 @@ private static function getOptions(string $options): array $optionsInArray = explode('&', $options); foreach ($optionsInArray as $option) { - list($key, $value) = explode('=', $option); + [$key, $value] = explode('=', $option); if (\in_array($key, static::VALID_DSN_OPTIONS, true)) { $results[$key] = $value; diff --git a/src/Symfony/Component/Cache/Adapter/MemcachedAdapter.php b/src/Symfony/Component/Cache/Adapter/MemcachedAdapter.php index 038892eabc3ba..53ead840c4eee 100644 --- a/src/Symfony/Component/Cache/Adapter/MemcachedAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/MemcachedAdapter.php @@ -123,7 +123,7 @@ public static function createConnection($servers, array $options = []) } $params = preg_replace_callback('#^memcached:(//)?(?:([^@]*+)@)?#', function ($m) use (&$username, &$password) { if (!empty($m[2])) { - list($username, $password) = explode(':', $m[2], 2) + [1 => null]; + [$username, $password] = explode(':', $m[2], 2) + [1 => null]; } return 'file:'.($m[1] ?? ''); diff --git a/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php b/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php index 0abf787f44ddb..a8f8f3038a652 100644 --- a/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php @@ -401,7 +401,7 @@ private function initialize() if (2 !== \count($values) || !isset($values[0], $values[1])) { $this->keys = $this->values = []; } else { - list($this->keys, $this->values) = $values; + [$this->keys, $this->values] = $values; } } diff --git a/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php b/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php index 9527fa63ffae9..faba3d8822a75 100644 --- a/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php @@ -88,7 +88,7 @@ static function (CacheItemInterface $innerItem, array $item) { $item["\0*\0value"] = ["\x9D".pack('VN', (int) (0.1 + $metadata[self::METADATA_EXPIRY] - self::METADATA_EXPIRY_OFFSET), $metadata[self::METADATA_CTIME])."\x5F" => $item["\0*\0value"]]; } $innerItem->set($item["\0*\0value"]); - $innerItem->expiresAt(null !== $item["\0*\0expiry"] ? \DateTime::createFromFormat('U.u', sprintf('%.6f', 0 === $item["\0*\0expiry"] ? \PHP_INT_MAX : $item["\0*\0expiry"])) : null); + $innerItem->expiresAt(null !== $item["\0*\0expiry"] ? \DateTime::createFromFormat('U.u', sprintf('%.6F', 0 === $item["\0*\0expiry"] ? \PHP_INT_MAX : $item["\0*\0expiry"])) : null); }, null, CacheItem::class diff --git a/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php b/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php index 57c355eaa43a6..d7fe3ebc60b9e 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php @@ -118,9 +118,9 @@ public function testGetMetadata() $metadata = $item->getMetadata(); $this->assertArrayHasKey(CacheItem::METADATA_CTIME, $metadata); - $this->assertEqualsWithDelta(1000, $metadata[CacheItem::METADATA_CTIME], 6); + $this->assertEqualsWithDelta(999, $metadata[CacheItem::METADATA_CTIME], 10); $this->assertArrayHasKey(CacheItem::METADATA_EXPIRY, $metadata); - $this->assertEqualsWithDelta(9.5 + time(), $metadata[CacheItem::METADATA_EXPIRY], 0.6); + $this->assertEqualsWithDelta(9 + time(), $metadata[CacheItem::METADATA_EXPIRY], 1); } public function testDefaultLifeTime() diff --git a/src/Symfony/Component/Cache/Tests/Adapter/CouchbaseBucketAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/CouchbaseBucketAdapterTest.php index 120d0d94c0cc5..c72d6710f22e9 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/CouchbaseBucketAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/CouchbaseBucketAdapterTest.php @@ -16,7 +16,8 @@ use Symfony\Component\Cache\Adapter\CouchbaseBucketAdapter; /** - * @requires extension couchbase 2.6.0 + * @requires extension couchbase <3.0.0 + * @requires extension couchbase >=2.6.0 * @group integration * * @author Antonio Jose Cerezo Aranda @@ -32,6 +33,10 @@ class CouchbaseBucketAdapterTest extends AdapterTestCase public static function setupBeforeClass(): void { + if (!CouchbaseBucketAdapter::isSupported()) { + self::markTestSkipped('Couchbase >= 2.6.0 < 3.0.0 is required.'); + } + self::$client = AbstractAdapter::createConnection('couchbase://'.getenv('COUCHBASE_HOST').'/cache', ['username' => getenv('COUCHBASE_USER'), 'password' => getenv('COUCHBASE_PASS')] ); diff --git a/src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterTest.php index 69f334656d58e..58318f6a25294 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterTest.php @@ -155,7 +155,7 @@ public function save(CacheItemInterface $item): bool $this->keys[$key] = $id = \count($this->values); $this->data[$key] = $this->values[$id] = $item->get(); $this->warmUp($this->data); - list($this->keys, $this->values) = eval(substr(file_get_contents($this->file), 6)); + [$this->keys, $this->values] = eval(substr(file_get_contents($this->file), 6)); }, $this, PhpArrayAdapter::class))(); return true; diff --git a/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterSentinelTest.php b/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterSentinelTest.php index 09f563036bc2f..82b9f08b65474 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterSentinelTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterSentinelTest.php @@ -37,7 +37,7 @@ public static function setUpBeforeClass(): void public function testInvalidDSNHasBothClusterAndSentinel() { $this->expectException('Symfony\Component\Cache\Exception\InvalidArgumentException'); - $this->expectExceptionMessage('Invalid Redis DSN: cannot use both redis_cluster and redis_sentinel at the same time'); + $this->expectExceptionMessage('Cannot use both "redis_cluster" and "redis_sentinel" at the same time:'); $dsn = 'redis:?host[redis1]&host[redis2]&host[redis3]&redis_cluster=1&redis_sentinel=mymaster'; RedisAdapter::createConnection($dsn); } diff --git a/src/Symfony/Component/Cache/Tests/DataCollector/CacheDataCollectorTest.php b/src/Symfony/Component/Cache/Tests/DataCollector/CacheDataCollectorTest.php new file mode 100644 index 0000000000000..f704bbfe0e49f --- /dev/null +++ b/src/Symfony/Component/Cache/Tests/DataCollector/CacheDataCollectorTest.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Marshaller; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Cache\Adapter\TraceableAdapter; +use Symfony\Component\Cache\DataCollector\CacheDataCollector; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class CacheDataCollectorTest extends TestCase +{ + private const INSTANCE_NAME = 'test'; + + public function testEmptyDataCollector() + { + $statistics = $this->getCacheDataCollectorStatisticsFromEvents([]); + + $this->assertEquals($statistics[self::INSTANCE_NAME]['calls'], 0, 'calls'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['reads'], 0, 'reads'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['hits'], 0, 'hits'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['misses'], 0, 'misses'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['writes'], 0, 'writes'); + } + + public function testOneEventDataCollector() + { + $traceableAdapterEvent = new \stdClass(); + $traceableAdapterEvent->name = 'getItem'; + $traceableAdapterEvent->start = 0; + $traceableAdapterEvent->end = 0; + $traceableAdapterEvent->hits = 0; + + $statistics = $this->getCacheDataCollectorStatisticsFromEvents([$traceableAdapterEvent]); + + $this->assertEquals($statistics[self::INSTANCE_NAME]['calls'], 1, 'calls'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['reads'], 1, 'reads'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['hits'], 0, 'hits'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['misses'], 1, 'misses'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['writes'], 0, 'writes'); + } + + public function testHitedEventDataCollector() + { + $traceableAdapterEvent = new \stdClass(); + $traceableAdapterEvent->name = 'hasItem'; + $traceableAdapterEvent->start = 0; + $traceableAdapterEvent->end = 0; + $traceableAdapterEvent->hits = 1; + $traceableAdapterEvent->misses = 0; + $traceableAdapterEvent->result = ['foo' => false]; + + $statistics = $this->getCacheDataCollectorStatisticsFromEvents([$traceableAdapterEvent]); + + $this->assertEquals($statistics[self::INSTANCE_NAME]['calls'], 1, 'calls'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['reads'], 1, 'reads'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['hits'], 1, 'hits'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['misses'], 0, 'misses'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['writes'], 0, 'writes'); + } + + public function testSavedEventDataCollector() + { + $traceableAdapterEvent = new \stdClass(); + $traceableAdapterEvent->name = 'save'; + $traceableAdapterEvent->start = 0; + $traceableAdapterEvent->end = 0; + + $statistics = $this->getCacheDataCollectorStatisticsFromEvents([$traceableAdapterEvent]); + + $this->assertEquals($statistics[self::INSTANCE_NAME]['calls'], 1, 'calls'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['reads'], 0, 'reads'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['hits'], 0, 'hits'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['misses'], 0, 'misses'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['writes'], 1, 'writes'); + } + + private function getCacheDataCollectorStatisticsFromEvents(array $traceableAdapterEvents) + { + $traceableAdapterMock = $this->createMock(TraceableAdapter::class); + $traceableAdapterMock->method('getCalls')->willReturn($traceableAdapterEvents); + + $cacheDataCollector = new CacheDataCollector(); + $cacheDataCollector->addInstance(self::INSTANCE_NAME, $traceableAdapterMock); + $cacheDataCollector->collect(new Request(), new Response()); + + return $cacheDataCollector->getStatistics(); + } +} diff --git a/src/Symfony/Component/Cache/Traits/AbstractAdapterTrait.php b/src/Symfony/Component/Cache/Traits/AbstractAdapterTrait.php index de3dbfc7a8b3c..86c1aac489dd9 100644 --- a/src/Symfony/Component/Cache/Traits/AbstractAdapterTrait.php +++ b/src/Symfony/Component/Cache/Traits/AbstractAdapterTrait.php @@ -14,6 +14,7 @@ use Psr\Cache\CacheItemInterface; use Psr\Log\LoggerAwareTrait; use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; /** * @author Nicolas Grekas diff --git a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php index 468656e333fa3..34b8aa73a5fc1 100644 --- a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php +++ b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php @@ -111,7 +111,7 @@ public static function createConnection($servers, array $options = []) } $params = preg_replace_callback('#^memcached:(//)?(?:([^@]*+)@)?#', function ($m) use (&$username, &$password) { if (!empty($m[2])) { - list($username, $password) = explode(':', $m[2], 2) + [1 => null]; + [$username, $password] = explode(':', $m[2], 2) + [1 => null]; } return 'file:'.($m[1] ?? ''); diff --git a/src/Symfony/Component/Cache/Traits/RedisTrait.php b/src/Symfony/Component/Cache/Traits/RedisTrait.php index c7701fbecc147..543e6e9045c15 100644 --- a/src/Symfony/Component/Cache/Traits/RedisTrait.php +++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php @@ -469,7 +469,7 @@ private function pipeline(\Closure $generator, $redis = null): \Generator foreach ($connections as $h => $c) { $connections[$h] = $c[0]->exec(); } - foreach ($results as $k => list($h, $c)) { + foreach ($results as $k => [$h, $c]) { $results[$k] = $connections[$h][$c]; } } else { diff --git a/src/Symfony/Component/Cache/composer.json b/src/Symfony/Component/Cache/composer.json index af7827f2bb432..2df9ff4c1fc9c 100644 --- a/src/Symfony/Component/Cache/composer.json +++ b/src/Symfony/Component/Cache/composer.json @@ -38,6 +38,7 @@ "symfony/config": "^4.4|^5.0", "symfony/dependency-injection": "^4.4|^5.0", "symfony/filesystem": "^4.4|^5.0", + "symfony/http-kernel": "^4.4|^5.0", "symfony/var-dumper": "^4.4|^5.0" }, "conflict": { diff --git a/src/Symfony/Component/Config/Definition/ArrayNode.php b/src/Symfony/Component/Config/Definition/ArrayNode.php index 2a828caf78538..5b1aec59c5926 100644 --- a/src/Symfony/Component/Config/Definition/ArrayNode.php +++ b/src/Symfony/Component/Config/Definition/ArrayNode.php @@ -333,7 +333,7 @@ protected function normalizeValue($value) */ protected function remapXml(array $value) { - foreach ($this->xmlRemappings as list($singular, $plural)) { + foreach ($this->xmlRemappings as [$singular, $plural]) { if (!isset($value[$singular])) { continue; } diff --git a/src/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php b/src/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php index f686c53b5718f..ef60eff374dba 100644 --- a/src/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php +++ b/src/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php @@ -53,7 +53,7 @@ private function writeNode(NodeInterface $node, int $depth = 0, bool $root = fal }); if (\count($remapping)) { - list($singular) = current($remapping); + [$singular] = current($remapping); $rootName = $singular; } } diff --git a/src/Symfony/Component/Config/Resource/GlobResource.php b/src/Symfony/Component/Config/Resource/GlobResource.php index 21777e648eb45..c77e837c245f9 100644 --- a/src/Symfony/Component/Config/Resource/GlobResource.php +++ b/src/Symfony/Component/Config/Resource/GlobResource.php @@ -94,6 +94,14 @@ public function __sleep(): array return ['prefix', 'pattern', 'recursive', 'hash', 'forExclusion', 'excludedPrefixes']; } + /** + * @internal + */ + public function __wakeup(): void + { + $this->globBrace = \defined('GLOB_BRACE') ? \GLOB_BRACE : 0; + } + public function getIterator(): \Traversable { if (!file_exists($this->prefix) || (!$this->recursive && '' === $this->pattern)) { diff --git a/src/Symfony/Component/Config/Tests/Resource/GlobResourceTest.php b/src/Symfony/Component/Config/Tests/Resource/GlobResourceTest.php index 2b6609d740c86..a30fbe8c4339b 100644 --- a/src/Symfony/Component/Config/Tests/Resource/GlobResourceTest.php +++ b/src/Symfony/Component/Config/Tests/Resource/GlobResourceTest.php @@ -194,4 +194,17 @@ public function testUnbalancedBraceFallback() $this->assertSame([], array_keys(iterator_to_array($resource))); } + + public function testSerializeUnserialize() + { + $dir = \dirname(__DIR__).\DIRECTORY_SEPARATOR.'Fixtures'; + $resource = new GlobResource($dir, '/Resource', true); + + $newResource = unserialize(serialize($resource)); + + $p = new \ReflectionProperty($resource, 'globBrace'); + $p->setAccessible(true); + + $this->assertEquals($p->getValue($resource), $p->getValue($newResource)); + } } diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatter.php b/src/Symfony/Component/Console/Formatter/OutputFormatter.php index edc6a1d71d6f9..4ad248868dbfc 100644 --- a/src/Symfony/Component/Console/Formatter/OutputFormatter.php +++ b/src/Symfony/Component/Console/Formatter/OutputFormatter.php @@ -25,6 +25,14 @@ class OutputFormatter implements WrappableOutputFormatterInterface private $styles = []; private $styleStack; + public function __clone() + { + $this->styleStack = clone $this->styleStack; + foreach ($this->styles as $key => $value) { + $this->styles[$key] = clone $value; + } + } + /** * Escapes "<" special char in given text. * diff --git a/src/Symfony/Component/Console/Helper/Table.php b/src/Symfony/Component/Console/Helper/Table.php index fee5a416b73d9..21848e65db03b 100644 --- a/src/Symfony/Component/Console/Helper/Table.php +++ b/src/Symfony/Component/Console/Helper/Table.php @@ -418,13 +418,13 @@ private function renderRowSeparator(int $type = self::SEPARATOR_MID, string $tit $crossings = $this->style->getCrossingChars(); if (self::SEPARATOR_MID === $type) { - list($horizontal, $leftChar, $midChar, $rightChar) = [$borders[2], $crossings[8], $crossings[0], $crossings[4]]; + [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[2], $crossings[8], $crossings[0], $crossings[4]]; } elseif (self::SEPARATOR_TOP === $type) { - list($horizontal, $leftChar, $midChar, $rightChar) = [$borders[0], $crossings[1], $crossings[2], $crossings[3]]; + [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[1], $crossings[2], $crossings[3]]; } elseif (self::SEPARATOR_TOP_BOTTOM === $type) { - list($horizontal, $leftChar, $midChar, $rightChar) = [$borders[0], $crossings[9], $crossings[10], $crossings[11]]; + [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[9], $crossings[10], $crossings[11]]; } else { - list($horizontal, $leftChar, $midChar, $rightChar) = [$borders[0], $crossings[7], $crossings[6], $crossings[5]]; + [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[7], $crossings[6], $crossings[5]]; } $markup = $leftChar; diff --git a/src/Symfony/Component/Console/Output/ConsoleOutput.php b/src/Symfony/Component/Console/Output/ConsoleOutput.php index 96664f1508abb..2cda213a04b09 100644 --- a/src/Symfony/Component/Console/Output/ConsoleOutput.php +++ b/src/Symfony/Component/Console/Output/ConsoleOutput.php @@ -41,6 +41,13 @@ public function __construct(int $verbosity = self::VERBOSITY_NORMAL, bool $decor { parent::__construct($this->openOutputStream(), $verbosity, $decorated, $formatter); + if (null === $formatter) { + // for BC reasons, stdErr has it own Formatter only when user don't inject a specific formatter. + $this->stderr = new StreamOutput($this->openErrorStream(), $verbosity, $decorated); + + return; + } + $actualDecorated = $this->isDecorated(); $this->stderr = new StreamOutput($this->openErrorStream(), $verbosity, $decorated, $this->getFormatter()); diff --git a/src/Symfony/Component/Console/Output/TrimmedBufferOutput.php b/src/Symfony/Component/Console/Output/TrimmedBufferOutput.php new file mode 100644 index 0000000000000..a03aa835f0086 --- /dev/null +++ b/src/Symfony/Component/Console/Output/TrimmedBufferOutput.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * A BufferedOutput that keeps only the last N chars. + * + * @author Jérémy Derussé + */ +class TrimmedBufferOutput extends Output +{ + private $maxLength; + private $buffer = ''; + + public function __construct( + int $maxLength, + ?int $verbosity = self::VERBOSITY_NORMAL, + bool $decorated = false, + OutputFormatterInterface $formatter = null + ) { + if ($maxLength <= 0) { + throw new InvalidArgumentException(sprintf('"%s()" expects a strictly positive maxLength. Got %d.', __METHOD__, $maxLength)); + } + + parent::__construct($verbosity, $decorated, $formatter); + $this->maxLength = $maxLength; + } + + /** + * Empties buffer and returns its content. + * + * @return string + */ + public function fetch() + { + $content = $this->buffer; + $this->buffer = ''; + + return $content; + } + + /** + * {@inheritdoc} + */ + protected function doWrite($message, $newline) + { + $this->buffer .= $message; + + if ($newline) { + $this->buffer .= \PHP_EOL; + } + + $this->buffer = substr($this->buffer, 0 - $this->maxLength); + } +} diff --git a/src/Symfony/Component/Console/Style/SymfonyStyle.php b/src/Symfony/Component/Console/Style/SymfonyStyle.php index fa4dfa018bd15..eb455cda2ea1d 100644 --- a/src/Symfony/Component/Console/Style/SymfonyStyle.php +++ b/src/Symfony/Component/Console/Style/SymfonyStyle.php @@ -21,8 +21,8 @@ use Symfony\Component\Console\Helper\TableCell; use Symfony\Component\Console\Helper\TableSeparator; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\BufferedOutput; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\TrimmedBufferOutput; use Symfony\Component\Console\Question\ChoiceQuestion; use Symfony\Component\Console\Question\ConfirmationQuestion; use Symfony\Component\Console\Question\Question; @@ -46,7 +46,7 @@ class SymfonyStyle extends OutputStyle public function __construct(InputInterface $input, OutputInterface $output) { $this->input = $input; - $this->bufferedOutput = new BufferedOutput($output->getVerbosity(), false, clone $output->getFormatter()); + $this->bufferedOutput = new TrimmedBufferOutput(\DIRECTORY_SEPARATOR === '\\' ? 4 : 2, $output->getVerbosity(), false, clone $output->getFormatter()); // Windows cmd wraps lines as soon as the terminal width is reached, whether there are following chars or not. $width = (new Terminal())->getWidth() ?: self::MAX_LINE_LENGTH; $this->lineLength = min($width - (int) (\DIRECTORY_SEPARATOR === '\\'), self::MAX_LINE_LENGTH); @@ -444,9 +444,8 @@ private function autoPrependText(): void private function writeBuffer(string $message, bool $newLine, int $type): void { - // We need to know if the two last chars are PHP_EOL - // Preserve the last 4 chars inserted (PHP_EOL on windows is two chars) in the history buffer - $this->bufferedOutput->write(substr($message, -4), $newLine, $type); + // We need to know if the last chars are PHP_EOL + $this->bufferedOutput->write($message, $newLine, $type); } private function createBlock(iterable $messages, string $type = null, string $style = null, string $prefix = ' ', bool $padding = false, bool $escape = false): array diff --git a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_20.php b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_20.php new file mode 100644 index 0000000000000..6b47969eeeba6 --- /dev/null +++ b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_20.php @@ -0,0 +1,13 @@ +setDecorated(true); + $output = new SymfonyStyle($input, $output); + $output->write('do you want something'); + $output->writeln('?'); +}; diff --git a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_20.txt b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_20.txt new file mode 100644 index 0000000000000..c082985309229 --- /dev/null +++ b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_20.txt @@ -0,0 +1 @@ +do you want something? diff --git a/src/Symfony/Component/Console/Tests/Output/ConsoleOutputTest.php b/src/Symfony/Component/Console/Tests/Output/ConsoleOutputTest.php index db39a02b8a616..33a5371d6e4b9 100644 --- a/src/Symfony/Component/Console/Tests/Output/ConsoleOutputTest.php +++ b/src/Symfony/Component/Console/Tests/Output/ConsoleOutputTest.php @@ -18,11 +18,19 @@ class ConsoleOutputTest extends TestCase { - public function testConstructor() + public function testConstructorWithoutFormatter() { $output = new ConsoleOutput(Output::VERBOSITY_QUIET, true); $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument'); - $this->assertSame($output->getFormatter(), $output->getErrorOutput()->getFormatter(), '__construct() takes a formatter or null as the third argument'); + $this->assertNotSame($output->getFormatter(), $output->getErrorOutput()->getFormatter(), 'ErrorOutput should use it own formatter'); + } + + public function testConstructorWithFormatter() + { + $output = new ConsoleOutput(Output::VERBOSITY_QUIET, true, $formatter = new OutputFormatter()); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument'); + $this->assertSame($formatter, $output->getFormatter()); + $this->assertSame($formatter, $output->getErrorOutput()->getFormatter(), 'Output and ErrorOutput should use the same provided formatter'); } public function testSetFormatter() @@ -31,6 +39,7 @@ public function testSetFormatter() $outputFormatter = new OutputFormatter(); $output->setFormatter($outputFormatter); $this->assertSame($outputFormatter, $output->getFormatter()); + $this->assertSame($outputFormatter, $output->getErrorOutput()->getFormatter()); } public function testSetVerbosity() diff --git a/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php b/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php index 943b94172a609..16bb2baec4ac7 100644 --- a/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php +++ b/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php @@ -14,8 +14,10 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\NullOutput; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Console\Tester\CommandTester; @@ -115,4 +117,18 @@ public function testGetErrorStyleUsesTheCurrentOutputIfNoErrorOutputIsAvailable( $this->assertInstanceOf(SymfonyStyle::class, $style->getErrorStyle()); } + + public function testMemoryConsumption() + { + $io = new SymfonyStyle(new ArrayInput([]), new NullOutput()); + $str = 'teststr'; + $io->writeln($str, SymfonyStyle::VERBOSITY_QUIET); + $io->writeln($str, SymfonyStyle::VERBOSITY_QUIET); + $start = memory_get_usage(); + for ($i = 0; $i < 100; ++$i) { + $io->writeln($str, SymfonyStyle::VERBOSITY_QUIET); + } + + $this->assertSame(0, memory_get_usage() - $start); + } } diff --git a/src/Symfony/Component/CssSelector/Parser/Parser.php b/src/Symfony/Component/CssSelector/Parser/Parser.php index e8a46c062ce7f..a03f1527f144b 100644 --- a/src/Symfony/Component/CssSelector/Parser/Parser.php +++ b/src/Symfony/Component/CssSelector/Parser/Parser.php @@ -113,7 +113,7 @@ private function parseSelectorList(TokenStream $stream): array private function parserSelectorNode(TokenStream $stream): Node\SelectorNode { - list($result, $pseudoElement) = $this->parseSimpleSelector($stream); + [$result, $pseudoElement] = $this->parseSimpleSelector($stream); while (true) { $stream->skipWhitespace(); @@ -134,7 +134,7 @@ private function parserSelectorNode(TokenStream $stream): Node\SelectorNode $combinator = ' '; } - list($nextSelector, $pseudoElement) = $this->parseSimpleSelector($stream); + [$nextSelector, $pseudoElement] = $this->parseSimpleSelector($stream); $result = new Node\CombinedSelectorNode($result, $combinator, $nextSelector); } @@ -209,7 +209,7 @@ private function parseSimpleSelector(TokenStream $stream, bool $insideNegation = throw SyntaxErrorException::nestedNot(); } - list($argument, $argumentPseudoElement) = $this->parseSimpleSelector($stream, true); + [$argument, $argumentPseudoElement] = $this->parseSimpleSelector($stream, true); $next = $stream->getNext(); if (null !== $argumentPseudoElement) { diff --git a/src/Symfony/Component/CssSelector/XPath/Extension/FunctionExtension.php b/src/Symfony/Component/CssSelector/XPath/Extension/FunctionExtension.php index 2b79aaafc9fcb..d3f7222a4d0ab 100644 --- a/src/Symfony/Component/CssSelector/XPath/Extension/FunctionExtension.php +++ b/src/Symfony/Component/CssSelector/XPath/Extension/FunctionExtension.php @@ -51,7 +51,7 @@ public function getFunctionTranslators(): array public function translateNthChild(XPathExpr $xpath, FunctionNode $function, bool $last = false, bool $addNameTest = true): XPathExpr { try { - list($a, $b) = Parser::parseSeries($function->getArguments()); + [$a, $b] = Parser::parseSeries($function->getArguments()); } catch (SyntaxErrorException $e) { throw new ExpressionErrorException(sprintf('Invalid series: "%s".', implode('", "', $function->getArguments())), 0, $e); } diff --git a/src/Symfony/Component/DependencyInjection/Argument/BoundArgument.php b/src/Symfony/Component/DependencyInjection/Argument/BoundArgument.php index 60059267beea4..f3535d68b0fe5 100644 --- a/src/Symfony/Component/DependencyInjection/Argument/BoundArgument.php +++ b/src/Symfony/Component/DependencyInjection/Argument/BoundArgument.php @@ -54,9 +54,9 @@ public function getValues(): array public function setValues(array $values) { if (5 === \count($values)) { - list($this->value, $this->identifier, $this->used, $this->type, $this->file) = $values; + [$this->value, $this->identifier, $this->used, $this->type, $this->file] = $values; } else { - list($this->value, $this->identifier, $this->used) = $values; + [$this->value, $this->identifier, $this->used] = $values; } } } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php index 76c954a987245..85478da5e9b16 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php @@ -127,7 +127,7 @@ protected function getConstructor(Definition $definition, bool $required) } if ($factory) { - list($class, $method) = $factory; + [$class, $method] = $factory; if ($class instanceof Reference) { $class = $this->container->findDefinition((string) $class)->getClass(); } elseif ($class instanceof Definition) { diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php index beb488236c069..f7dbe6c8a35a4 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php @@ -12,6 +12,7 @@ namespace Symfony\Component\DependencyInjection\Compiler; use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; @@ -35,6 +36,7 @@ class AnalyzeServiceReferencesPass extends AbstractRecursivePass private $hasProxyDumper; private $lazy; private $byConstructor; + private $byFactory; private $definitions; private $aliases; @@ -58,6 +60,7 @@ public function process(ContainerBuilder $container) $this->graph->clear(); $this->lazy = false; $this->byConstructor = false; + $this->byFactory = false; $this->definitions = $container->getDefinitions(); $this->aliases = $container->getAliases(); @@ -79,7 +82,7 @@ protected function processValue($value, bool $isRoot = false) $inExpression = $this->inExpression(); if ($value instanceof ArgumentInterface) { - $this->lazy = true; + $this->lazy = !$this->byFactory || !$value instanceof IteratorArgument; parent::processValue($value->getValues()); $this->lazy = $lazy; @@ -129,7 +132,11 @@ protected function processValue($value, bool $isRoot = false) $byConstructor = $this->byConstructor; $this->byConstructor = $isRoot || $byConstructor; + + $byFactory = $this->byFactory; + $this->byFactory = true; $this->processValue($value->getFactory()); + $this->byFactory = $byFactory; $this->processValue($value->getArguments()); $properties = $value->getProperties(); diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index ba10e92b1e7c3..d201b58b987aa 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -126,7 +126,7 @@ private function doProcessValue($value, bool $isRoot = false) $this->methodCalls = $this->autowireCalls($reflectionClass, $isRoot); if ($constructor) { - list(, $arguments) = array_shift($this->methodCalls); + [, $arguments] = array_shift($this->methodCalls); if ($arguments !== $value->getArguments()) { $value->setArguments($arguments); @@ -152,7 +152,7 @@ private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot): foreach ($this->methodCalls as $i => $call) { $this->decoratedMethodIndex = $i; - list($method, $arguments) = $call; + [$method, $arguments] = $call; if ($method instanceof \ReflectionFunctionAbstract) { $reflectionMethod = $method; diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredMethodsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredMethodsPass.php index 2c774f781371c..3ae92beb9aec3 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredMethodsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredMethodsPass.php @@ -37,7 +37,7 @@ protected function processValue($value, bool $isRoot = false) $alreadyCalledMethods = []; $withers = []; - foreach ($value->getMethodCalls() as list($method)) { + foreach ($value->getMethodCalls() as [$method]) { $alreadyCalledMethods[strtolower($method)] = true; } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php b/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php index c9931fb72619e..ac2dc2299f70b 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php @@ -46,9 +46,9 @@ public function process(ContainerBuilder $container) } $decoratingDefinitions = []; - foreach ($definitions as list($id, $definition)) { + foreach ($definitions as [$id, $definition]) { $decoratedService = $definition->getDecoratedService(); - list($inner, $renamedId) = $decoratedService; + [$inner, $renamedId] = $decoratedService; $invalidBehavior = $decoratedService[3] ?? ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; $definition->setDecoratedService(null); diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php index b86c1b786477b..61e99d73becf9 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php @@ -41,11 +41,11 @@ public function process(ContainerBuilder $container) try { parent::process($container); - foreach ($this->unusedBindings as list($key, $serviceId, $bindingType, $file)) { + foreach ($this->unusedBindings as [$key, $serviceId, $bindingType, $file]) { $argumentType = $argumentName = $message = null; if (false !== strpos($key, ' ')) { - list($argumentType, $argumentName) = explode(' ', $key, 2); + [$argumentType, $argumentName] = explode(' ', $key, 2); } elseif ('$' === $key[0]) { $argumentName = $key; } else { @@ -117,7 +117,7 @@ protected function processValue($value, bool $isRoot = false) $bindingNames = []; foreach ($bindings as $key => $binding) { - list($bindingValue, $bindingId, $used, $bindingType, $file) = $binding->getValues(); + [$bindingValue, $bindingId, $used, $bindingType, $file] = $binding->getValues(); if ($used) { $this->usedBindings[$bindingId] = true; unset($this->unusedBindings[$bindingId]); @@ -156,7 +156,7 @@ protected function processValue($value, bool $isRoot = false) } foreach ($calls as $i => $call) { - list($method, $arguments) = $call; + [$method, $arguments] = $call; if ($method instanceof \ReflectionFunctionAbstract) { $reflectionMethod = $method; @@ -210,7 +210,7 @@ protected function processValue($value, bool $isRoot = false) } if ($constructor) { - list(, $arguments) = array_pop($calls); + [, $arguments] = array_pop($calls); if ($arguments !== $value->getArguments()) { $value->setArguments($arguments); @@ -229,7 +229,7 @@ protected function processValue($value, bool $isRoot = false) */ private function getBindingValue(BoundArgument $binding) { - list($bindingValue, $bindingId) = $binding->getValues(); + [$bindingValue, $bindingId] = $binding->getValues(); $this->usedBindings[$bindingId] = true; unset($this->unusedBindings[$bindingId]); diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php index 60d059fb29445..4aa33aad1ac26 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php @@ -35,12 +35,22 @@ public function process(ContainerBuilder $container) } } + $tagsToKeep = []; + + if ($container->hasParameter('container.behavior_describing_tags')) { + $tagsToKeep = $container->getParameter('container.behavior_describing_tags'); + } + foreach ($container->getDefinitions() as $id => $definition) { - $container->setDefinition($id, $this->processDefinition($container, $id, $definition)); + $container->setDefinition($id, $this->processDefinition($container, $id, $definition, $tagsToKeep)); + } + + if ($container->hasParameter('container.behavior_describing_tags')) { + $container->getParameterBag()->remove('container.behavior_describing_tags'); } } - private function processDefinition(ContainerBuilder $container, string $id, Definition $definition): Definition + private function processDefinition(ContainerBuilder $container, string $id, Definition $definition, array $tagsToKeep): Definition { $instanceofConditionals = $definition->getInstanceofConditionals(); $autoconfiguredInstanceof = $definition->isAutoconfigured() ? $container->getAutoconfiguredInstanceof() : []; @@ -114,10 +124,10 @@ private function processDefinition(ContainerBuilder $container, string $id, Defi } // Don't add tags to service decorators - if (null === $definition->getDecoratedService()) { - $i = \count($instanceofTags); - while (0 <= --$i) { - foreach ($instanceofTags[$i] as $k => $v) { + $i = \count($instanceofTags); + while (0 <= --$i) { + foreach ($instanceofTags[$i] as $k => $v) { + if (null === $definition->getDecoratedService() || \in_array($k, $tagsToKeep, true)) { foreach ($v as $v) { if ($definition->hasTag($k) && \in_array($v, $definition->getTag($k))) { continue; diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php index fd3c5e4d1d9f1..c1c5748e8d601 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php @@ -41,7 +41,7 @@ protected function processValue($value, bool $isRoot = false) $calls[] = ['__construct', $value->getArguments()]; foreach ($calls as $i => $call) { - list($method, $arguments) = $call; + [$method, $arguments] = $call; $parameters = null; $resolvedArguments = []; @@ -107,7 +107,7 @@ protected function processValue($value, bool $isRoot = false) } } - list(, $arguments) = array_pop($calls); + [, $arguments] = array_pop($calls); if ($arguments !== $value->getArguments()) { $value->setArguments($arguments); diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index ec82969d1bb4c..5267f6fbb9ba9 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -1167,7 +1167,7 @@ private function doResolveServices($value, array &$inlineServices = [], bool $is return $this->resolveServices($reference); }; } elseif ($value instanceof IteratorArgument) { - $value = new RewindableGenerator(function () use ($value) { + $value = new RewindableGenerator(function () use ($value, &$inlineServices) { foreach ($value->getValues() as $k => $v) { foreach (self::getServiceConditionals($v) as $s) { if (!$this->has($s)) { @@ -1175,12 +1175,12 @@ private function doResolveServices($value, array &$inlineServices = [], bool $is } } foreach (self::getInitializedConditionals($v) as $s) { - if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)) { + if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE, $inlineServices)) { continue 2; } } - yield $k => $this->resolveServices($v); + yield $k => $this->doResolveServices($v, $inlineServices); } }, function () use ($value): int { $count = 0; @@ -1455,7 +1455,7 @@ public function removeBindings(string $id) { if ($this->hasDefinition($id)) { foreach ($this->getDefinition($id)->getBindings() as $key => $binding) { - list(, $bindingId) = $binding->getValues(); + [, $bindingId] = $binding->getValues(); $this->removedBindingIds[(int) $bindingId] = true; } } diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 93b303ea2bf20..5376f38cc83ad 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -185,25 +185,7 @@ public function dump(array $options = []) } } - (new AnalyzeServiceReferencesPass(false, !$this->getProxyDumper() instanceof NullDumper))->process($this->container); - $checkedNodes = []; - $this->circularReferences = []; - $this->singleUsePrivateIds = []; - foreach ($this->container->getCompiler()->getServiceReferenceGraph()->getNodes() as $id => $node) { - if (!$node->getValue() instanceof Definition) { - continue; - } - if (!isset($checkedNodes[$id])) { - $this->analyzeCircularReferences($id, $node->getOutEdges(), $checkedNodes); - } - if ($this->isSingleUsePrivateNode($node)) { - $this->singleUsePrivateIds[$id] = $id; - } - } - $this->container->getCompiler()->getServiceReferenceGraph()->clear(); - $checkedNodes = []; - $this->singleUsePrivateIds = array_diff_key($this->singleUsePrivateIds, $this->circularReferences); - + $this->analyzeReferences(); $this->docStar = $options['debug'] ? '*' : ''; if (!empty($options['file']) && is_dir($dir = \dirname($options['file']))) { @@ -429,61 +411,99 @@ private function getProxyDumper(): ProxyDumper return $this->proxyDumper; } - /** - * @param ServiceReferenceGraphEdge[] $edges - */ - private function analyzeCircularReferences(string $sourceId, array $edges, array &$checkedNodes, array &$currentPath = [], bool $byConstructor = true) + private function analyzeReferences() { - $checkedNodes[$sourceId] = true; - $currentPath[$sourceId] = $byConstructor; - - foreach ($edges as $edge) { - $node = $edge->getDestNode(); - $id = $node->getId(); - - if (!$node->getValue() instanceof Definition || $sourceId === $id || $edge->isLazy() || $edge->isWeak()) { - // no-op - } elseif (isset($currentPath[$id])) { - $this->addCircularReferences($id, $currentPath, $edge->isReferencedByConstructor()); - } elseif (!isset($checkedNodes[$id])) { - $this->analyzeCircularReferences($id, $node->getOutEdges(), $checkedNodes, $currentPath, $edge->isReferencedByConstructor()); - } elseif (isset($this->circularReferences[$id])) { - $this->connectCircularReferences($id, $currentPath, $edge->isReferencedByConstructor()); + (new AnalyzeServiceReferencesPass(false, !$this->getProxyDumper() instanceof NullDumper))->process($this->container); + $checkedNodes = []; + $this->circularReferences = []; + $this->singleUsePrivateIds = []; + foreach ($this->container->getCompiler()->getServiceReferenceGraph()->getNodes() as $id => $node) { + if (!$node->getValue() instanceof Definition) { + continue; } - } - unset($currentPath[$sourceId]); - } - - private function connectCircularReferences(string $sourceId, array &$currentPath, bool $byConstructor, array &$subPath = []) - { - $currentPath[$sourceId] = $subPath[$sourceId] = $byConstructor; - foreach ($this->circularReferences[$sourceId] as $id => $byConstructor) { - if (isset($currentPath[$id])) { - $this->addCircularReferences($id, $currentPath, $byConstructor); - } elseif (!isset($subPath[$id]) && isset($this->circularReferences[$id])) { - $this->connectCircularReferences($id, $currentPath, $byConstructor, $subPath); + if ($this->isSingleUsePrivateNode($node)) { + $this->singleUsePrivateIds[$id] = $id; } + + $this->collectCircularReferences($id, $node->getOutEdges(), $checkedNodes); } - unset($currentPath[$sourceId], $subPath[$sourceId]); + + $this->container->getCompiler()->getServiceReferenceGraph()->clear(); + $this->singleUsePrivateIds = array_diff_key($this->singleUsePrivateIds, $this->circularReferences); } - private function addCircularReferences(string $id, array $currentPath, bool $byConstructor) + private function collectCircularReferences(string $sourceId, array $edges, array &$checkedNodes, array &$loops = [], array $path = [], bool $byConstructor = true): void { - $currentPath[$id] = $byConstructor; - $circularRefs = []; + $path[$sourceId] = $byConstructor; + $checkedNodes[$sourceId] = true; + foreach ($edges as $edge) { + $node = $edge->getDestNode(); + $id = $node->getId(); + if ($sourceId === $id || !$node->getValue() instanceof Definition || $edge->isLazy() || $edge->isWeak()) { + continue; + } - foreach (array_reverse($currentPath) as $parentId => $v) { - $byConstructor = $byConstructor && $v; - $circularRefs[] = $parentId; + if (isset($path[$id])) { + $loop = null; + $loopByConstructor = $edge->isReferencedByConstructor(); + $pathInLoop = [$id, []]; + foreach ($path as $k => $pathByConstructor) { + if (null !== $loop) { + $loop[] = $k; + $pathInLoop[1][$k] = $pathByConstructor; + $loops[$k][] = &$pathInLoop; + $loopByConstructor = $loopByConstructor && $pathByConstructor; + } elseif ($k === $id) { + $loop = []; + } + } + $this->addCircularReferences($id, $loop, $loopByConstructor); + } elseif (!isset($checkedNodes[$id])) { + $this->collectCircularReferences($id, $node->getOutEdges(), $checkedNodes, $loops, $path, $edge->isReferencedByConstructor()); + } elseif (isset($loops[$id])) { + // we already had detected loops for this edge + // let's check if we have a common ancestor in one of the detected loops + foreach ($loops[$id] as [$first, $loopPath]) { + if (!isset($path[$first])) { + continue; + } + // We have a common ancestor, let's fill the current path + $fillPath = null; + foreach ($loopPath as $k => $pathByConstructor) { + if (null !== $fillPath) { + $fillPath[$k] = $pathByConstructor; + } elseif ($k === $id) { + $fillPath = $path; + $fillPath[$k] = $pathByConstructor; + } + } - if ($parentId === $id) { - break; + // we can now build the loop + $loop = null; + $loopByConstructor = $edge->isReferencedByConstructor(); + foreach ($fillPath as $k => $pathByConstructor) { + if (null !== $loop) { + $loop[] = $k; + $loopByConstructor = $loopByConstructor && $pathByConstructor; + } elseif ($k === $first) { + $loop = []; + } + } + $this->addCircularReferences($first, $loop, true); + break; + } } } + unset($path[$sourceId]); + } - $currentId = $id; - foreach ($circularRefs as $parentId) { + private function addCircularReferences(string $sourceId, array $currentPath, bool $byConstructor) + { + $currentId = $sourceId; + $currentPath = array_reverse($currentPath); + $currentPath[] = $currentId; + foreach ($currentPath as $parentId) { if (empty($this->circularReferences[$parentId][$currentId])) { $this->circularReferences[$parentId][$currentId] = $byConstructor; } @@ -597,7 +617,7 @@ private function addServiceInclude(string $cId, Definition $definition): string } } - foreach ($this->serviceCalls as $id => list($callCount, $behavior)) { + foreach ($this->serviceCalls as $id => [$callCount, $behavior]) { if ('service_container' !== $id && $id !== $cId && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $behavior && $this->container->has($id) @@ -950,7 +970,7 @@ private function addInlineReference(string $id, Definition $definition, string $ $targetId = (string) $this->container->getAlias($targetId); } - list($callCount, $behavior) = $this->serviceCalls[$targetId]; + [$callCount, $behavior] = $this->serviceCalls[$targetId]; if ($id === $targetId) { return $this->addInlineService($id, $definition, $definition); @@ -1006,7 +1026,7 @@ private function addInlineService(string $id, Definition $definition, Definition $code = ''; if ($isSimpleInstance = $isRootInstance = null === $inlineDef) { - foreach ($this->serviceCalls as $targetId => list($callCount, $behavior, $byConstructor)) { + foreach ($this->serviceCalls as $targetId => [$callCount, $behavior, $byConstructor]) { if ($byConstructor && isset($this->circularReferences[$id][$targetId]) && !$this->circularReferences[$id][$targetId]) { $code .= $this->addInlineReference($id, $definition, $targetId, $forConstructor); } @@ -1076,7 +1096,7 @@ private function addServices(array &$services = null): string } foreach ($definitions as $id => $definition) { - if (!(list($file, $code) = $services[$id]) || null !== $file) { + if (!([$file, $code] = $services[$id]) || null !== $file) { continue; } if ($definition->isPublic()) { @@ -1094,7 +1114,7 @@ private function generateServiceFiles(array $services): iterable $definitions = $this->container->getDefinitions(); ksort($definitions); foreach ($definitions as $id => $definition) { - if ((list($file, $code) = $services[$id]) && null !== $file && ($definition->isPublic() || !$this->isTrivialInstance($definition) || isset($this->locatedIds[$id]))) { + if (([$file, $code] = $services[$id]) && null !== $file && ($definition->isPublic() || !$this->isTrivialInstance($definition) || isset($this->locatedIds[$id]))) { yield $file => [$code, $definition->hasTag($this->hotPathTag) || !$definition->hasTag($this->preloadTags[1]) && !$definition->isDeprecated() && !$definition->hasErrors()]; } } @@ -1796,7 +1816,7 @@ private function dumpValue($value, bool $interpolate = true): string return sprintf('new \%s($this->getService, [%s%s], [%s%s])', ServiceLocator::class, $serviceMap, $serviceMap ? "\n " : '', $serviceTypes, $serviceTypes ? "\n " : ''); } } finally { - list($this->definitionVariables, $this->referenceVariables) = $scope; + [$this->definitionVariables, $this->referenceVariables] = $scope; } } elseif ($value instanceof Definition) { if ($value->hasErrors() && $e = $value->getErrors()) { diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php index 2a0ee95de63d9..537a4994fb19a 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php @@ -118,7 +118,7 @@ private function addService(Definition $definition, ?string $id, \DOMElement $pa $service->setAttribute('lazy', 'true'); } if (null !== $decoratedService = $definition->getDecoratedService()) { - list($decorated, $renamedId, $priority) = $decoratedService; + [$decorated, $renamedId, $priority] = $decoratedService; $service->setAttribute('decorates', $decorated); $decorationOnInvalid = $decoratedService[3] ?? ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; diff --git a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php index ca57cbac20108..5f7080f090143 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php @@ -138,7 +138,7 @@ private function addService(string $id, Definition $definition): string } if (null !== $decoratedService = $definition->getDecoratedService()) { - list($decorated, $renamedId, $priority) = $decoratedService; + [$decorated, $renamedId, $priority] = $decoratedService; $code .= sprintf(" decorates: %s\n", $decorated); if (null !== $renamedId) { $code .= sprintf(" decoration_inner_name: %s\n", $renamedId); diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 33b5a009f7b4c..42103275f5391 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -430,7 +430,7 @@ private function processAnonymousServices(\DOMDocument $xml, string $file) // resolve definitions uksort($definitions, 'strnatcmp'); - foreach (array_reverse($definitions) as $id => list($domElement, $file)) { + foreach (array_reverse($definitions) as $id => [$domElement, $file]) { if (null !== $definition = $this->parseDefinition($domElement, $file, new Definition())) { $this->setDefinition($id, $definition); } @@ -624,14 +624,13 @@ public function validateSchema(\DOMDocument $dom) EOF ; - if (\LIBXML_VERSION < 20900) { + if ($this->shouldEnableEntityLoader()) { $disableEntities = libxml_disable_entity_loader(false); $valid = @$dom->schemaValidateSource($source); libxml_disable_entity_loader($disableEntities); } else { $valid = @$dom->schemaValidateSource($source); } - foreach ($tmpfiles as $tmpfile) { @unlink($tmpfile); } @@ -639,6 +638,36 @@ public function validateSchema(\DOMDocument $dom) return $valid; } + private function shouldEnableEntityLoader(): bool + { + // Version prior to 8.0 can be enabled without deprecation + if (\PHP_VERSION_ID < 80000) { + return true; + } + + static $dom, $schema; + if (null === $dom) { + $dom = new \DOMDocument(); + $dom->loadXML(''); + + $tmpfile = tempnam(sys_get_temp_dir(), 'symfony'); + register_shutdown_function(static function () use ($tmpfile) { + @unlink($tmpfile); + }); + $schema = ' + + +'; + file_put_contents($tmpfile, ' + + + +'); + } + + return !@$dom->schemaValidateSource($schema); + } + private function validateAlias(\DOMElement $alias, string $file) { foreach ($alias->attributes as $name => $node) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php index 3acfbed776f1f..99aa65b13869b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php @@ -12,12 +12,16 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler; use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\ResourceInterface; +use Symfony\Component\Config\ResourceCheckerInterface; use Symfony\Component\DependencyInjection\Argument\BoundArgument; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\ResolveChildDefinitionsPass; use Symfony\Component\DependencyInjection\Compiler\ResolveInstanceofConditionalsPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Contracts\Service\ResetInterface; +use Symfony\Contracts\Service\ServiceSubscriberInterface; class ResolveInstanceofConditionalsPassTest extends TestCase { @@ -325,4 +329,60 @@ public function testDecoratorsAreNotAutomaticallyTagged() $this->assertSame(['manual' => [[]]], $container->getDefinition('decorator')->getTags()); } + + public function testDecoratorsKeepBehaviorDescribingTags() + { + $container = new ContainerBuilder(); + + $container->setParameter('container.behavior_describing_tags', [ + 'container.service_subscriber', + 'kernel.reset', + ]); + + $container->register('decorator', DecoratorWithBehavior::class) + ->setAutoconfigured(true) + ->setDecoratedService('decorated') + ; + + $container->registerForAutoconfiguration(ResourceCheckerInterface::class) + ->addTag('config_cache.resource_checker') + ; + $container->registerForAutoconfiguration(ServiceSubscriberInterface::class) + ->addTag('container.service_subscriber') + ; + $container->registerForAutoconfiguration(ResetInterface::class) + ->addTag('kernel.reset', ['method' => 'reset']) + ; + + (new ResolveInstanceofConditionalsPass())->process($container); + + $this->assertEquals([ + 'container.service_subscriber' => [0 => []], + 'kernel.reset' => [ + [ + 'method' => 'reset', + ], + ], + ], $container->getDefinition('decorator')->getTags()); + $this->assertFalse($container->hasParameter('container.behavior_describing_tags')); + } +} + +class DecoratorWithBehavior implements ResetInterface, ResourceCheckerInterface, ServiceSubscriberInterface +{ + public function reset() + { + } + + public function supports(ResourceInterface $metadata) + { + } + + public function isFresh(ResourceInterface $resource, $timestamp) + { + } + + public static function getSubscribedServices() + { + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveParameterPlaceHoldersPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveParameterPlaceHoldersPassTest.php index 2504ff112cf86..c586e72c2acbc 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveParameterPlaceHoldersPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveParameterPlaceHoldersPassTest.php @@ -67,7 +67,7 @@ public function testAliasParametersShouldBeResolved() public function testBindingsShouldBeResolved() { - list($boundValue) = $this->container->getDefinition('foo')->getBindings()['$baz']->getValues(); + [$boundValue] = $this->container->getDefinition('foo')->getBindings()['$baz']->getValues(); $this->assertSame($this->container->getParameterBag()->resolveValue('%env(BAZ)%'), $boundValue); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index e974f7c2b3a30..8a2d454c5d63d 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -1395,6 +1395,19 @@ public function testUninitializedReference() public function testAlmostCircular($visibility) { $container = include __DIR__.'/Fixtures/containers/container_almost_circular.php'; + $container->compile(); + + $entityManager = $container->get('doctrine.entity_manager'); + $this->assertEquals(new \stdClass(), $entityManager); + + $pA = $container->get('pA'); + $this->assertEquals(new \stdClass(), $pA); + + $logger = $container->get('monolog.logger'); + $this->assertEquals(new \stdClass(), $logger->handler); + + $logger_inline = $container->get('monolog_inline.logger'); + $this->assertEquals(new \stdClass(), $logger_inline->handler); $foo = $container->get('foo'); $this->assertSame($foo, $foo->bar->foobar->foo); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 1335d60e6963d..dfbd14e94df58 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -1061,6 +1061,18 @@ public function testAlmostCircular($visibility) $container = new $container(); + $entityManager = $container->get('doctrine.entity_manager'); + $this->assertEquals(new \stdClass(), $entityManager); + + $pA = $container->get('pA'); + $this->assertEquals(new \stdClass(), $pA); + + $logger = $container->get('monolog.logger'); + $this->assertEquals(new \stdClass(), $logger->handler); + + $logger_inline = $container->get('monolog_inline.logger'); + $this->assertEquals(new \stdClass(), $logger_inline->handler); + $foo = $container->get('foo'); $this->assertSame($foo, $foo->bar->foobar->foo); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_almost_circular.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_almost_circular.php index a1f885399bd58..8dd05316969f2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_almost_circular.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_almost_circular.php @@ -1,5 +1,7 @@ register('doctrine.config', 'stdClass')->setPublic(false) + ->setProperty('resolver', new Reference('doctrine.entity_listener_resolver')) + ->setProperty('flag', 'ok'); + +$container->register('doctrine.entity_manager', 'stdClass')->setPublic(true) + ->setFactory([FactoryChecker::class, 'create']) + ->addArgument(new Reference('doctrine.config')); +$container->register('doctrine.entity_listener_resolver', 'stdClass')->setPublic($public) + ->addArgument(new IteratorArgument([new Reference('doctrine.listener')])); +$container->register('doctrine.listener', 'stdClass')->setPublic($public) + ->addArgument(new Reference('doctrine.entity_manager')); + +// multiple path detection + +$container->register('pA', 'stdClass')->setPublic(true) + ->addArgument(new Reference('pB')) + ->addArgument(new Reference('pC')); + +$container->register('pB', 'stdClass')->setPublic($public) + ->setProperty('d', new Reference('pD')); +$container->register('pC', 'stdClass')->setPublic($public) + ->setLazy(true) + ->setProperty('d', new Reference('pD')); + +$container->register('pD', 'stdClass')->setPublic($public) + ->addArgument(new Reference('pA')); + +// monolog-like + handler that require monolog + +$container->register('monolog.logger', 'stdClass')->setPublic(true) + ->setProperty('handler', new Reference('mailer.transport')); + +$container->register('mailer.transport', 'stdClass')->setPublic($public) + ->setFactory([new Reference('mailer.transport_factory'), 'create']); + +$container->register('mailer.transport_factory', FactoryCircular::class)->setPublic($public) + ->addArgument(new TaggedIteratorArgument('mailer.transport')); + +$container->register('mailer.transport_factory.amazon', 'stdClass')->setPublic($public) + ->addArgument(new Reference('monolog.logger_2')) + ->addTag('mailer.transport'); + +$container->register('monolog.logger_2', 'stdClass')->setPublic($public) + ->setProperty('handler', new Reference('mailer.transport')); + +// monolog-like + handler that require monolog with inlined factory + +$container->register('monolog_inline.logger', 'stdClass')->setPublic(true) + ->setProperty('handler', new Reference('mailer_inline.mailer')); + +$container->register('mailer_inline.mailer', 'stdClass')->setPublic(false) + ->addArgument( + (new Definition('stdClass')) + ->setFactory([new Reference('mailer_inline.transport_factory'), 'create']) + ); + +$container->register('mailer_inline.transport_factory', FactoryCircular::class)->setPublic($public) + ->addArgument(new TaggedIteratorArgument('mailer_inline.transport')); + +$container->register('mailer_inline.transport_factory.amazon', 'stdClass')->setPublic($public) + ->addArgument(new Reference('monolog_inline.logger_2')) + ->addTag('mailer.transport'); + +$container->register('monolog_inline.logger_2', 'stdClass')->setPublic($public) + ->setProperty('handler', new Reference('mailer_inline.mailer')); + // same visibility for deps $container->register('foo', FooCircular::class)->setPublic(true) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php index fee80ac779749..39ad97f990e93 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php @@ -111,6 +111,35 @@ public function __construct($lazyValues, $lazyEmptyValues) } } +class FactoryCircular +{ + public $services; + + public function __construct($services) + { + $this->services = $services; + } + + public function create() + { + foreach ($this->services as $service) { + return $service; + } + } +} + +class FactoryChecker +{ + public static function create($config) + { + if (!isset($config->flag)) { + throw new \LogicException('The injected config must contain a "flag" property.'); + } + + return new stdClass(); + } +} + class FoobarCircular { public function __construct(FooCircular $foo) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php index 60c60b3adfb45..f20be40568b0b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php @@ -25,6 +25,7 @@ public function __construct() 'baz6' => 'getBaz6Service', 'connection' => 'getConnectionService', 'connection2' => 'getConnection2Service', + 'doctrine.entity_manager' => 'getDoctrine_EntityManagerService', 'foo' => 'getFooService', 'foo2' => 'getFoo2Service', 'foo5' => 'getFoo5Service', @@ -36,6 +37,9 @@ public function __construct() 'manager' => 'getManagerService', 'manager2' => 'getManager2Service', 'manager3' => 'getManager3Service', + 'monolog.logger' => 'getMonolog_LoggerService', + 'monolog_inline.logger' => 'getMonologInline_LoggerService', + 'pA' => 'getPAService', 'root' => 'getRootService', 'subscriber' => 'getSubscriberService', ]; @@ -67,6 +71,9 @@ public function getRemovedIds(): array 'connection4' => true, 'dispatcher' => true, 'dispatcher2' => true, + 'doctrine.config' => true, + 'doctrine.entity_listener_resolver' => true, + 'doctrine.listener' => true, 'foo4' => true, 'foobar' => true, 'foobar2' => true, @@ -77,8 +84,19 @@ public function getRemovedIds(): array 'level5' => true, 'level6' => true, 'logger2' => true, + 'mailer.transport' => true, + 'mailer.transport_factory' => true, + 'mailer.transport_factory.amazon' => true, + 'mailer_inline.mailer' => true, + 'mailer_inline.transport_factory' => true, + 'mailer_inline.transport_factory.amazon' => true, 'manager4' => true, + 'monolog.logger_2' => true, + 'monolog_inline.logger_2' => true, 'multiuse1' => true, + 'pB' => true, + 'pC' => true, + 'pD' => true, 'subscriber2' => true, ]; } @@ -173,6 +191,22 @@ protected function getConnection2Service() return $instance; } + /** + * Gets the public 'doctrine.entity_manager' shared service. + * + * @return \stdClass + */ + protected function getDoctrine_EntityManagerService() + { + $a = new \stdClass(); + $a->resolver = new \stdClass(new RewindableGenerator(function () { + yield 0 => ($this->privates['doctrine.listener'] ?? $this->getDoctrine_ListenerService()); + }, 1)); + $a->flag = 'ok'; + + return $this->services['doctrine.entity_manager'] = \FactoryChecker::create($a); + } + /** * Gets the public 'foo' shared service. * @@ -352,6 +386,56 @@ protected function getManager3Service($lazyLoad = true) return $this->services['manager3'] = new \stdClass($b); } + /** + * Gets the public 'monolog.logger' shared service. + * + * @return \stdClass + */ + protected function getMonolog_LoggerService() + { + $this->services['monolog.logger'] = $instance = new \stdClass(); + + $instance->handler = ($this->privates['mailer.transport'] ?? $this->getMailer_TransportService()); + + return $instance; + } + + /** + * Gets the public 'monolog_inline.logger' shared service. + * + * @return \stdClass + */ + protected function getMonologInline_LoggerService() + { + $this->services['monolog_inline.logger'] = $instance = new \stdClass(); + + $instance->handler = ($this->privates['mailer_inline.mailer'] ?? $this->getMailerInline_MailerService()); + + return $instance; + } + + /** + * Gets the public 'pA' shared service. + * + * @return \stdClass + */ + protected function getPAService() + { + $a = new \stdClass(); + + $b = ($this->privates['pC'] ?? $this->getPCService()); + + if (isset($this->services['pA'])) { + return $this->services['pA']; + } + + $this->services['pA'] = $instance = new \stdClass($a, $b); + + $a->d = ($this->privates['pD'] ?? $this->getPDService()); + + return $instance; + } + /** * Gets the public 'root' shared service. * @@ -400,6 +484,16 @@ protected function getBar6Service() return $this->privates['bar6'] = new \stdClass($a); } + /** + * Gets the private 'doctrine.listener' shared service. + * + * @return \stdClass + */ + protected function getDoctrine_ListenerService() + { + return $this->privates['doctrine.listener'] = new \stdClass(($this->services['doctrine.entity_manager'] ?? $this->getDoctrine_EntityManagerService())); + } + /** * Gets the private 'level5' shared service. * @@ -416,6 +510,60 @@ protected function getLevel5Service() return $instance; } + /** + * Gets the private 'mailer.transport' shared service. + * + * @return \stdClass + */ + protected function getMailer_TransportService() + { + return $this->privates['mailer.transport'] = (new \FactoryCircular(new RewindableGenerator(function () { + yield 0 => ($this->privates['mailer.transport_factory.amazon'] ?? $this->getMailer_TransportFactory_AmazonService()); + yield 1 => $this->getMailerInline_TransportFactory_AmazonService(); + }, 2)))->create(); + } + + /** + * Gets the private 'mailer.transport_factory.amazon' shared service. + * + * @return \stdClass + */ + protected function getMailer_TransportFactory_AmazonService() + { + $a = new \stdClass(); + + $this->privates['mailer.transport_factory.amazon'] = $instance = new \stdClass($a); + + $a->handler = ($this->privates['mailer.transport'] ?? $this->getMailer_TransportService()); + + return $instance; + } + + /** + * Gets the private 'mailer_inline.mailer' shared service. + * + * @return \stdClass + */ + protected function getMailerInline_MailerService() + { + return $this->privates['mailer_inline.mailer'] = new \stdClass((new \FactoryCircular(new RewindableGenerator(function () { + return new \EmptyIterator(); + }, 0)))->create()); + } + + /** + * Gets the private 'mailer_inline.transport_factory.amazon' shared service. + * + * @return \stdClass + */ + protected function getMailerInline_TransportFactory_AmazonService() + { + $a = new \stdClass(); + $a->handler = ($this->privates['mailer_inline.mailer'] ?? $this->getMailerInline_MailerService()); + + return new \stdClass($a); + } + /** * Gets the private 'manager4' shared service. * @@ -431,4 +579,34 @@ protected function getManager4Service($lazyLoad = true) return $instance; } + + /** + * Gets the private 'pC' shared service. + * + * @return \stdClass + */ + protected function getPCService($lazyLoad = true) + { + $this->privates['pC'] = $instance = new \stdClass(); + + $instance->d = ($this->privates['pD'] ?? $this->getPDService()); + + return $instance; + } + + /** + * Gets the private 'pD' shared service. + * + * @return \stdClass + */ + protected function getPDService() + { + $a = ($this->services['pA'] ?? $this->getPAService()); + + if (isset($this->privates['pD'])) { + return $this->privates['pD']; + } + + return $this->privates['pD'] = new \stdClass($a); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php index 4905ade2dd408..666ac0a876995 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php @@ -30,6 +30,9 @@ public function __construct() 'connection4' => 'getConnection4Service', 'dispatcher' => 'getDispatcherService', 'dispatcher2' => 'getDispatcher2Service', + 'doctrine.entity_listener_resolver' => 'getDoctrine_EntityListenerResolverService', + 'doctrine.entity_manager' => 'getDoctrine_EntityManagerService', + 'doctrine.listener' => 'getDoctrine_ListenerService', 'foo' => 'getFooService', 'foo2' => 'getFoo2Service', 'foo4' => 'getFoo4Service', @@ -42,9 +45,22 @@ public function __construct() 'listener3' => 'getListener3Service', 'listener4' => 'getListener4Service', 'logger' => 'getLoggerService', + 'mailer.transport' => 'getMailer_TransportService', + 'mailer.transport_factory' => 'getMailer_TransportFactoryService', + 'mailer.transport_factory.amazon' => 'getMailer_TransportFactory_AmazonService', + 'mailer_inline.transport_factory' => 'getMailerInline_TransportFactoryService', + 'mailer_inline.transport_factory.amazon' => 'getMailerInline_TransportFactory_AmazonService', 'manager' => 'getManagerService', 'manager2' => 'getManager2Service', 'manager3' => 'getManager3Service', + 'monolog.logger' => 'getMonolog_LoggerService', + 'monolog.logger_2' => 'getMonolog_Logger2Service', + 'monolog_inline.logger' => 'getMonologInline_LoggerService', + 'monolog_inline.logger_2' => 'getMonologInline_Logger2Service', + 'pA' => 'getPAService', + 'pB' => 'getPBService', + 'pC' => 'getPCService', + 'pD' => 'getPDService', 'root' => 'getRootService', 'subscriber' => 'getSubscriberService', ]; @@ -71,12 +87,14 @@ public function getRemovedIds(): array 'bar6' => true, 'config' => true, 'config2' => true, + 'doctrine.config' => true, 'level2' => true, 'level3' => true, 'level4' => true, 'level5' => true, 'level6' => true, 'logger2' => true, + 'mailer_inline.mailer' => true, 'manager4' => true, 'multiuse1' => true, 'subscriber2' => true, @@ -248,6 +266,42 @@ protected function getDispatcher2Service($lazyLoad = true) return $instance; } + /** + * Gets the public 'doctrine.entity_listener_resolver' shared service. + * + * @return \stdClass + */ + protected function getDoctrine_EntityListenerResolverService() + { + return $this->services['doctrine.entity_listener_resolver'] = new \stdClass(new RewindableGenerator(function () { + yield 0 => ($this->services['doctrine.listener'] ?? $this->getDoctrine_ListenerService()); + }, 1)); + } + + /** + * Gets the public 'doctrine.entity_manager' shared service. + * + * @return \stdClass + */ + protected function getDoctrine_EntityManagerService() + { + $a = new \stdClass(); + $a->resolver = ($this->services['doctrine.entity_listener_resolver'] ?? $this->getDoctrine_EntityListenerResolverService()); + $a->flag = 'ok'; + + return $this->services['doctrine.entity_manager'] = \FactoryChecker::create($a); + } + + /** + * Gets the public 'doctrine.listener' shared service. + * + * @return \stdClass + */ + protected function getDoctrine_ListenerService() + { + return $this->services['doctrine.listener'] = new \stdClass(($this->services['doctrine.entity_manager'] ?? $this->getDoctrine_EntityManagerService())); + } + /** * Gets the public 'foo' shared service. * @@ -434,6 +488,61 @@ protected function getLoggerService() return $instance; } + /** + * Gets the public 'mailer.transport' shared service. + * + * @return \stdClass + */ + protected function getMailer_TransportService() + { + return $this->services['mailer.transport'] = ($this->services['mailer.transport_factory'] ?? $this->getMailer_TransportFactoryService())->create(); + } + + /** + * Gets the public 'mailer.transport_factory' shared service. + * + * @return \FactoryCircular + */ + protected function getMailer_TransportFactoryService() + { + return $this->services['mailer.transport_factory'] = new \FactoryCircular(new RewindableGenerator(function () { + yield 0 => ($this->services['mailer.transport_factory.amazon'] ?? $this->getMailer_TransportFactory_AmazonService()); + yield 1 => ($this->services['mailer_inline.transport_factory.amazon'] ?? $this->getMailerInline_TransportFactory_AmazonService()); + }, 2)); + } + + /** + * Gets the public 'mailer.transport_factory.amazon' shared service. + * + * @return \stdClass + */ + protected function getMailer_TransportFactory_AmazonService() + { + return $this->services['mailer.transport_factory.amazon'] = new \stdClass(($this->services['monolog.logger_2'] ?? $this->getMonolog_Logger2Service())); + } + + /** + * Gets the public 'mailer_inline.transport_factory' shared service. + * + * @return \FactoryCircular + */ + protected function getMailerInline_TransportFactoryService() + { + return $this->services['mailer_inline.transport_factory'] = new \FactoryCircular(new RewindableGenerator(function () { + return new \EmptyIterator(); + }, 0)); + } + + /** + * Gets the public 'mailer_inline.transport_factory.amazon' shared service. + * + * @return \stdClass + */ + protected function getMailerInline_TransportFactory_AmazonService() + { + return $this->services['mailer_inline.transport_factory.amazon'] = new \stdClass(($this->services['monolog_inline.logger_2'] ?? $this->getMonologInline_Logger2Service())); + } + /** * Gets the public 'manager' shared service. * @@ -482,6 +591,127 @@ protected function getManager3Service($lazyLoad = true) return $this->services['manager3'] = new \stdClass($a); } + /** + * Gets the public 'monolog.logger' shared service. + * + * @return \stdClass + */ + protected function getMonolog_LoggerService() + { + $this->services['monolog.logger'] = $instance = new \stdClass(); + + $instance->handler = ($this->services['mailer.transport'] ?? $this->getMailer_TransportService()); + + return $instance; + } + + /** + * Gets the public 'monolog.logger_2' shared service. + * + * @return \stdClass + */ + protected function getMonolog_Logger2Service() + { + $this->services['monolog.logger_2'] = $instance = new \stdClass(); + + $instance->handler = ($this->services['mailer.transport'] ?? $this->getMailer_TransportService()); + + return $instance; + } + + /** + * Gets the public 'monolog_inline.logger' shared service. + * + * @return \stdClass + */ + protected function getMonologInline_LoggerService() + { + $this->services['monolog_inline.logger'] = $instance = new \stdClass(); + + $instance->handler = ($this->privates['mailer_inline.mailer'] ?? $this->getMailerInline_MailerService()); + + return $instance; + } + + /** + * Gets the public 'monolog_inline.logger_2' shared service. + * + * @return \stdClass + */ + protected function getMonologInline_Logger2Service() + { + $this->services['monolog_inline.logger_2'] = $instance = new \stdClass(); + + $instance->handler = ($this->privates['mailer_inline.mailer'] ?? $this->getMailerInline_MailerService()); + + return $instance; + } + + /** + * Gets the public 'pA' shared service. + * + * @return \stdClass + */ + protected function getPAService() + { + $a = ($this->services['pB'] ?? $this->getPBService()); + + if (isset($this->services['pA'])) { + return $this->services['pA']; + } + $b = ($this->services['pC'] ?? $this->getPCService()); + + if (isset($this->services['pA'])) { + return $this->services['pA']; + } + + return $this->services['pA'] = new \stdClass($a, $b); + } + + /** + * Gets the public 'pB' shared service. + * + * @return \stdClass + */ + protected function getPBService() + { + $this->services['pB'] = $instance = new \stdClass(); + + $instance->d = ($this->services['pD'] ?? $this->getPDService()); + + return $instance; + } + + /** + * Gets the public 'pC' shared service. + * + * @return \stdClass + */ + protected function getPCService($lazyLoad = true) + { + $this->services['pC'] = $instance = new \stdClass(); + + $instance->d = ($this->services['pD'] ?? $this->getPDService()); + + return $instance; + } + + /** + * Gets the public 'pD' shared service. + * + * @return \stdClass + */ + protected function getPDService() + { + $a = ($this->services['pA'] ?? $this->getPAService()); + + if (isset($this->services['pD'])) { + return $this->services['pD']; + } + + return $this->services['pD'] = new \stdClass($a); + } + /** * Gets the public 'root' shared service. * @@ -546,6 +776,16 @@ protected function getLevel5Service() return $instance; } + /** + * Gets the private 'mailer_inline.mailer' shared service. + * + * @return \stdClass + */ + protected function getMailerInline_MailerService() + { + return $this->privates['mailer_inline.mailer'] = new \stdClass(($this->services['mailer_inline.transport_factory'] ?? $this->getMailerInline_TransportFactoryService())->create()); + } + /** * Gets the private 'manager4' shared service. * diff --git a/src/Symfony/Component/Dotenv/Dotenv.php b/src/Symfony/Component/Dotenv/Dotenv.php index 8cb2ba82c278c..21aa729091a7b 100644 --- a/src/Symfony/Component/Dotenv/Dotenv.php +++ b/src/Symfony/Component/Dotenv/Dotenv.php @@ -456,7 +456,7 @@ private function resolveCommands(string $value, array $loadedVars): string $process = method_exists(Process::class, 'fromShellCommandline') ? Process::fromShellCommandline('echo '.$matches[0]) : new Process('echo '.$matches[0]); - if (!method_exists(Process::class, 'fromShellCommandline')) { + if (!method_exists(Process::class, 'fromShellCommandline') && method_exists(Process::class, 'inheritEnvironmentVariables')) { // Symfony 3.4 does not inherit env vars by default: $process->inheritEnvironmentVariables(); } diff --git a/src/Symfony/Component/ErrorHandler/DebugClassLoader.php b/src/Symfony/Component/ErrorHandler/DebugClassLoader.php index 747db80d805e1..2c216cbe7f850 100644 --- a/src/Symfony/Component/ErrorHandler/DebugClassLoader.php +++ b/src/Symfony/Component/ErrorHandler/DebugClassLoader.php @@ -493,7 +493,7 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array $hasCall = $refl->hasMethod('__call'); $hasStaticCall = $refl->hasMethod('__callStatic'); foreach (self::$method[$use] as $method) { - list($interface, $name, $static, $description) = $method; + [$interface, $name, $static, $description] = $method; if ($static ? $hasStaticCall : $hasCall) { continue; } @@ -555,12 +555,12 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array } if ($parent && isset(self::$finalMethods[$parent][$method->name])) { - list($declaringClass, $message) = self::$finalMethods[$parent][$method->name]; + [$declaringClass, $message] = self::$finalMethods[$parent][$method->name]; $deprecations[] = sprintf('The "%s::%s()" method is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".', $declaringClass, $method->name, $message, $className); } if (isset(self::$internalMethods[$class][$method->name])) { - list($declaringClass, $message) = self::$internalMethods[$class][$method->name]; + [$declaringClass, $message] = self::$internalMethods[$class][$method->name]; if (strncmp($ns, $declaringClass, $len)) { $deprecations[] = sprintf('The "%s::%s()" method is considered internal%s. It may change without further notice. You should not extend it from "%s".', $declaringClass, $method->name, $message, $className); } @@ -600,7 +600,7 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array } if (null !== ($returnType = self::$returnTypes[$class][$method->name] ?? self::MAGIC_METHODS[$method->name] ?? null) && !$method->hasReturnType() && !($doc && preg_match('/\n\s+\* @return +(\S+)/', $doc))) { - list($normalizedType, $returnType, $declaringClass, $declaringFile) = \is_string($returnType) ? [$returnType, $returnType, '', ''] : $returnType; + [$normalizedType, $returnType, $declaringClass, $declaringFile] = \is_string($returnType) ? [$returnType, $returnType, '', ''] : $returnType; if ('void' === $normalizedType) { $canAddReturnType = false; @@ -668,7 +668,7 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array $definedParameters[$parameter->name] = true; } } - foreach ($matches as list(, $parameterType, $parameterName)) { + foreach ($matches as [, $parameterType, $parameterName]) { if (!isset($definedParameters[$parameterName])) { $parameterType = trim($parameterType); self::$annotatedParameters[$class][$method->name][$parameterName] = sprintf('The "%%s::%s()" method will require a new "%s$%s" argument in the next major version of its %s "%s", not defining it is deprecated.', $method->name, $parameterType ? $parameterType.' ' : '', $parameterName, interface_exists($className) ? 'interface' : 'parent class', $className); @@ -939,10 +939,10 @@ private function patchMethod(\ReflectionMethod $method, string $returnType, stri continue; } - list($namespace, $useOffset, $useMap) = $useStatements[$file] ?? $useStatements[$file] = self::getUseStatements($file); + [$namespace, $useOffset, $useMap] = $useStatements[$file] ?? $useStatements[$file] = self::getUseStatements($file); if ('\\' !== $type[0]) { - list($declaringNamespace, , $declaringUseMap) = $useStatements[$declaringFile] ?? $useStatements[$declaringFile] = self::getUseStatements($declaringFile); + [$declaringNamespace, , $declaringUseMap] = $useStatements[$declaringFile] ?? $useStatements[$declaringFile] = self::getUseStatements($declaringFile); $p = strpos($type, '\\', 1); $alias = $p ? substr($type, 0, $p) : $type; diff --git a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php index 11dce4897bc65..87d538eaf8f04 100644 --- a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php +++ b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php @@ -177,7 +177,7 @@ public function getCalledListeners(Request $request = null) $hash = $request ? spl_object_hash($request) : null; $called = []; foreach ($this->callStack as $listener) { - list($eventName, $requestHash) = $this->callStack->getInfo(); + [$eventName, $requestHash] = $this->callStack->getInfo(); if (null === $hash || $hash === $requestHash) { $called[] = $listener->getInfo($eventName); } @@ -207,7 +207,7 @@ public function getNotCalledListeners(Request $request = null) if (null !== $this->callStack) { foreach ($this->callStack as $calledListener) { - list(, $requestHash) = $this->callStack->getInfo(); + [, $requestHash] = $this->callStack->getInfo(); if (null === $hash || $hash === $requestHash) { $calledListeners[] = $calledListener->getWrappedListener(); diff --git a/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php b/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php index ea9fe8c1b6cdc..173f4273eb2f4 100644 --- a/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php +++ b/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php @@ -19,10 +19,9 @@ class EventDispatcherTest extends TestCase { /* Some pseudo events */ - const preFoo = 'pre.foo'; - const postFoo = 'post.foo'; - const preBar = 'pre.bar'; - const postBar = 'post.bar'; + private const preFoo = 'pre.foo'; + private const postFoo = 'post.foo'; + private const preBar = 'pre.bar'; /** * @var EventDispatcher diff --git a/src/Symfony/Component/ExpressionLanguage/Lexer.php b/src/Symfony/Component/ExpressionLanguage/Lexer.php index 5a3fb0a0efc17..6e3cc488b1ee3 100644 --- a/src/Symfony/Component/ExpressionLanguage/Lexer.php +++ b/src/Symfony/Component/ExpressionLanguage/Lexer.php @@ -60,7 +60,7 @@ public function tokenize(string $expression) throw new SyntaxError(sprintf('Unexpected "%s".', $expression[$cursor]), $cursor, $expression); } - list($expect, $cur) = array_pop($brackets); + [$expect, $cur] = array_pop($brackets); if ($expression[$cursor] != strtr($expect, '([{', ')]}')) { throw new SyntaxError(sprintf('Unclosed "%s".', $expect), $cur, $expression); } @@ -92,7 +92,7 @@ public function tokenize(string $expression) $tokens[] = new Token(Token::EOF_TYPE, null, $cursor + 1); if (!empty($brackets)) { - list($expect, $cur) = array_pop($brackets); + [$expect, $cur] = array_pop($brackets); throw new SyntaxError(sprintf('Unclosed "%s".', $expect), $cur, $expression); } diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php index aaf62d02674f8..a1c53d88bb4b5 100644 --- a/src/Symfony/Component/Filesystem/Filesystem.php +++ b/src/Symfony/Component/Filesystem/Filesystem.php @@ -454,8 +454,8 @@ public function makePathRelative(string $endPath, string $startPath) return $result; }; - list($endPath, $endDriveLetter) = $splitDriveLetter($endPath); - list($startPath, $startDriveLetter) = $splitDriveLetter($startPath); + [$endPath, $endDriveLetter] = $splitDriveLetter($endPath); + [$startPath, $startDriveLetter] = $splitDriveLetter($startPath); $startPathArr = $splitPath($startPath); $endPathArr = $splitPath($endPath); @@ -590,7 +590,7 @@ public function isAbsolutePath(string $file) public function tempnam(string $dir, string $prefix/*, string $suffix = ''*/) { $suffix = \func_num_args() > 2 ? func_get_arg(2) : ''; - list($scheme, $hierarchy) = $this->getSchemeAndHierarchy($dir); + [$scheme, $hierarchy] = $this->getSchemeAndHierarchy($dir); // If no scheme or scheme is "file" or "gs" (Google Cloud) create temp file in local filesystem if ((null === $scheme || 'file' === $scheme || 'gs' === $scheme) && '' === $suffix) { @@ -658,13 +658,17 @@ public function dumpFile(string $filename, $content) // when the filesystem supports chmod. $tmpFile = $this->tempnam($dir, basename($filename)); - if (false === @file_put_contents($tmpFile, $content)) { - throw new IOException(sprintf('Failed to write file "%s".', $filename), 0, null, $filename); - } + try { + if (false === @file_put_contents($tmpFile, $content)) { + throw new IOException(sprintf('Failed to write file "%s".', $filename), 0, null, $filename); + } - @chmod($tmpFile, file_exists($filename) ? fileperms($filename) : 0666 & ~umask()); + @chmod($tmpFile, file_exists($filename) ? fileperms($filename) : 0666 & ~umask()); - $this->rename($tmpFile, $filename, true); + $this->rename($tmpFile, $filename, true); + } finally { + @unlink($tmpFile); + } } /** diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php index 68542d7b5c97d..4158f88f85aa9 100644 --- a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php +++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php @@ -1720,6 +1720,18 @@ public function testAppendToFileCreateTheFileIfNotExists() $this->assertStringEqualsFile($filename, 'bar'); } + public function testDumpRemovesTmpFilesOnFailure() + { + $expected = scandir(__DIR__, \SCANDIR_SORT_ASCENDING); + + try { + $this->filesystem->dumpFile(__DIR__.'/Fixtures', 'bar'); + $this->fail('IOException expected.'); + } catch (IOException $e) { + $this->assertSame($expected, scandir(__DIR__, \SCANDIR_SORT_ASCENDING)); + } + } + public function testDumpKeepsExistingPermissionsWhenOverwritingAnExistingFile() { $this->markAsSkippedIfChmodIsMissing(); diff --git a/src/Symfony/Component/Finder/Finder.php b/src/Symfony/Component/Finder/Finder.php index 2b13a73ceb42c..10cd62aacd1ba 100644 --- a/src/Symfony/Component/Finder/Finder.php +++ b/src/Symfony/Component/Finder/Finder.php @@ -655,7 +655,7 @@ public function append(iterable $iterator) } /** - * Check if the any results were found. + * Check if any results were found. * * @return bool */ diff --git a/src/Symfony/Component/Finder/Tests/Iterator/SortableIteratorTest.php b/src/Symfony/Component/Finder/Tests/Iterator/SortableIteratorTest.php index 5a47812bd3474..add8e7df596be 100644 --- a/src/Symfony/Component/Finder/Tests/Iterator/SortableIteratorTest.php +++ b/src/Symfony/Component/Finder/Tests/Iterator/SortableIteratorTest.php @@ -35,9 +35,10 @@ public function testAccept($mode, $expected) case SortableIterator::SORT_BY_ACCESSED_TIME: touch(self::toAbsolute('.git')); sleep(1); - file_get_contents(self::toAbsolute('.bar')); + touch(self::toAbsolute('.bar'), time()); break; case SortableIterator::SORT_BY_CHANGED_TIME: + sleep(1); file_put_contents(self::toAbsolute('test.php'), 'foo'); sleep(1); file_put_contents(self::toAbsolute('test.py'), 'foo'); diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php index 57b37ca21c638..f0aee23bfa057 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php @@ -12,8 +12,8 @@ namespace Symfony\Component\Form\Extension\Core\Type; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FileUploadError; use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Form\FormError; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormInterface; @@ -146,7 +146,7 @@ private function getFileUploadError(int $errorCode) $messageParameters = []; if (\UPLOAD_ERR_INI_SIZE === $errorCode) { - list($limitAsString, $suffix) = $this->factorizeSizes(0, self::getMaxFilesize()); + [$limitAsString, $suffix] = $this->factorizeSizes(0, self::getMaxFilesize()); $messageTemplate = 'The file is too large. Allowed maximum size is {{ limit }} {{ suffix }}.'; $messageParameters = [ '{{ limit }}' => $limitAsString, @@ -164,7 +164,7 @@ private function getFileUploadError(int $errorCode) $message = strtr($messageTemplate, $messageParameters); } - return new FormError($message, $messageTemplate, $messageParameters); + return new FileUploadError($message, $messageTemplate, $messageParameters); } /** diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TextareaType.php b/src/Symfony/Component/Form/Extension/Core/Type/TextareaType.php index 7db19d8aedc65..173b7ef53c8a2 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/TextareaType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/TextareaType.php @@ -23,6 +23,7 @@ class TextareaType extends AbstractType public function buildView(FormView $view, FormInterface $form, array $options) { $view->vars['pattern'] = null; + unset($view->vars['attr']['pattern']); } /** diff --git a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php index 1c63ecf5f4586..43eaeee4c829c 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php +++ b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php @@ -25,7 +25,6 @@ class FormValidator extends ConstraintValidator { private $resolvedGroups; - private $fieldFormConstraints; /** * {@inheritdoc} @@ -68,7 +67,6 @@ public function validate($form, Constraint $formConstraint) if ($hasChildren && $form->isRoot()) { $this->resolvedGroups = new \SplObjectStorage(); - $this->fieldFormConstraints = []; } if ($groups instanceof GroupSequence) { @@ -93,7 +91,6 @@ public function validate($form, Constraint $formConstraint) $this->resolvedGroups[$field] = (array) $group; $fieldFormConstraint = new Form(); $fieldFormConstraint->groups = $group; - $this->fieldFormConstraints[] = $fieldFormConstraint; $this->context->setNode($this->context->getValue(), $field, $this->context->getMetadata(), $this->context->getPropertyPath()); $validator->atPath(sprintf('children[%s]', $field->getName()))->validate($field, $fieldFormConstraint, $group); } @@ -139,10 +136,8 @@ public function validate($form, Constraint $formConstraint) foreach ($form->all() as $field) { if ($field->isSubmitted()) { $this->resolvedGroups[$field] = $groups; - $fieldFormConstraint = new Form(); - $this->fieldFormConstraints[] = $fieldFormConstraint; $this->context->setNode($this->context->getValue(), $field, $this->context->getMetadata(), $this->context->getPropertyPath()); - $validator->atPath(sprintf('children[%s]', $field->getName()))->validate($field, $fieldFormConstraint); + $validator->atPath(sprintf('children[%s]', $field->getName()))->validate($field, $formConstraint); } } } @@ -150,7 +145,6 @@ public function validate($form, Constraint $formConstraint) if ($hasChildren && $form->isRoot()) { // destroy storage to avoid memory leaks $this->resolvedGroups = new \SplObjectStorage(); - $this->fieldFormConstraints = []; } } elseif (!$form->isSynchronized()) { $childrenSynchronized = true; @@ -159,11 +153,8 @@ public function validate($form, Constraint $formConstraint) foreach ($form as $child) { if (!$child->isSynchronized()) { $childrenSynchronized = false; - - $fieldFormConstraint = new Form(); - $this->fieldFormConstraints[] = $fieldFormConstraint; $this->context->setNode($this->context->getValue(), $child, $this->context->getMetadata(), $this->context->getPropertyPath()); - $validator->atPath(sprintf('children[%s]', $child->getName()))->validate($child, $fieldFormConstraint); + $validator->atPath(sprintf('children[%s]', $child->getName()))->validate($child, $formConstraint); } } diff --git a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php index 12abef214bd76..c713ba2977383 100644 --- a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php +++ b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php @@ -11,12 +11,14 @@ namespace Symfony\Component\Form\Extension\Validator\ViolationMapper; +use Symfony\Component\Form\FileUploadError; use Symfony\Component\Form\FormError; use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\Util\InheritDataAwareIterator; use Symfony\Component\PropertyAccess\PropertyPathBuilder; use Symfony\Component\PropertyAccess\PropertyPathIterator; use Symfony\Component\PropertyAccess\PropertyPathIteratorInterface; +use Symfony\Component\Validator\Constraints\File; use Symfony\Component\Validator\ConstraintViolation; /** @@ -124,6 +126,23 @@ public function mapViolation(ConstraintViolation $violation, FormInterface $form // Only add the error if the form is synchronized if ($this->acceptsErrors($scope)) { + if ($violation->getConstraint() instanceof File && (string) \UPLOAD_ERR_INI_SIZE === $violation->getCode()) { + $errorsTarget = $scope; + + while (null !== $errorsTarget->getParent() && $errorsTarget->getConfig()->getErrorBubbling()) { + $errorsTarget = $errorsTarget->getParent(); + } + + $errors = $errorsTarget->getErrors(); + $errorsTarget->clearErrors(); + + foreach ($errors as $error) { + if (!$error instanceof FileUploadError) { + $errorsTarget->addError($error); + } + } + } + $scope->addError(new FormError( $violation->getMessage(), $violation->getMessageTemplate(), diff --git a/src/Symfony/Component/Form/FileUploadError.php b/src/Symfony/Component/Form/FileUploadError.php new file mode 100644 index 0000000000000..20142b20337ea --- /dev/null +++ b/src/Symfony/Component/Form/FileUploadError.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * @internal + */ +class FileUploadError extends FormError +{ +} diff --git a/src/Symfony/Component/Form/FormBuilder.php b/src/Symfony/Component/Form/FormBuilder.php index 53fbcb51963c2..8c92046ee3590 100644 --- a/src/Symfony/Component/Form/FormBuilder.php +++ b/src/Symfony/Component/Form/FormBuilder.php @@ -228,7 +228,7 @@ public function getIterator() */ private function resolveChild(string $name): FormBuilderInterface { - list($type, $options) = $this->unresolvedChildren[$name]; + [$type, $options] = $this->unresolvedChildren[$name]; unset($this->unresolvedChildren[$name]); diff --git a/src/Symfony/Component/Form/Resources/translations/validators.af.xlf b/src/Symfony/Component/Form/Resources/translations/validators.af.xlf new file mode 100644 index 0000000000000..58cd939cf793f --- /dev/null +++ b/src/Symfony/Component/Form/Resources/translations/validators.af.xlf @@ -0,0 +1,139 @@ + + + + + + This form should not contain extra fields. + Hierdie vorm moet nie ekstra velde bevat nie. + + + The uploaded file was too large. Please try to upload a smaller file. + Die opgelaaide lêer was te groot. Probeer asseblief 'n kleiner lêer. + + + The CSRF token is invalid. Please try to resubmit the form. + Die CSRF-teken is ongeldig. Probeer asseblief om die vorm weer in te dien. + + + This value is not a valid HTML5 color. + Hierdie waarde is nie 'n geldige HTML5 kleur nie. + + + Please enter a valid birthdate. + Voer asseblief 'n geldige geboortedatum in. + + + The selected choice is invalid. + Die gekiesde opsie is nie geldig nie. + + + The collection is invalid. + Die versameling is nie geldig nie. + + + Please select a valid color. + Kies asseblief 'n geldige kleur. + + + Please select a valid country. + Kies asseblief 'n geldige land. + + + Please select a valid currency. + Kies asseblief 'n geldige geldeenheid. + + + Please choose a valid date interval. + Kies asseblief 'n geldige datum interval. + + + Please enter a valid date and time. + Voer asseblilef 'n geldige datum en tyd in. + + + Please enter a valid date. + Voer asseblief 'n geldige datum in. + + + Please select a valid file. + Kies asseblief 'n geldige lêer. + + + The hidden field is invalid. + Die versteekte veld is nie geldig nie. + + + Please enter an integer. + Voer asseblief 'n geldige heeltal in. + + + Please select a valid language. + Kies assblief 'n geldige taal. + + + Please select a valid locale. + Voer assebliefn 'n geldige locale in. + + + Please enter a valid money amount. + Voer asseblief 'n geldige bedrag in. + + + Please enter a number. + Voer asseblief 'n nommer in. + + + The password is invalid. + Die wagwoord is ongeldig. + + + Please enter a percentage value. + Voer asseblief 'n geldige persentasie waarde in. + + + The values do not match. + Die waardes is nie dieselfde nie. + + + Please enter a valid time. + Voer asseblief 'n geldige tyd in time. + + + Please select a valid timezone. + Kies asseblief 'n geldige tydsone. + + + Please enter a valid URL. + Voer asseblief 'n geldige URL in. + + + Please enter a valid search term. + Voer asseblief 'n geldige soek term in. + + + Please provide a valid phone number. + Verskaf asseblief 'n geldige telefoonnommer. + + + The checkbox has an invalid value. + Die blokkie het 'n ongeldige waarde. + + + Please enter a valid email address. + Voer asseblief 'n geldige e-pos adres in. + + + Please select a valid option. + Kies asseblief 'n geldige opsie. + + + Please select a valid range. + Kies asseblief 'n geldige reeks. + + + Please enter a valid week. + Voer assblief 'n geldige week in. + + + + diff --git a/src/Symfony/Component/Form/Resources/translations/validators.az.xlf b/src/Symfony/Component/Form/Resources/translations/validators.az.xlf index 69e447385acda..b9269706db3e8 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.az.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.az.xlf @@ -14,6 +14,126 @@ The CSRF token is invalid. Please try to resubmit the form. CSRF nişanı yanlışdır. Lütfen formanı yenidən göndərin. + + This value is not a valid HTML5 color. + Bu dəyər doğru bir HTML5 rəngi deyil. + + + Please enter a valid birthdate. + Zəhmət olmasa doğru bir doğum günü daxil edin. + + + The selected choice is invalid. + Seçilmiş seçim doğru deyil. + + + The collection is invalid. + Kolleksiya doğru deyil. + + + Please select a valid color. + Zəhmət olmasa doğru bir rəng seçin. + + + Please select a valid country. + Zəhmət olmasa doğru bir ölkə seçin. + + + Please select a valid currency. + Zəhmət olmasa doğru bir valyuta seçin. + + + Please choose a valid date interval. + Zəhmət olmasa doğru bir tarix aralığı seçin. + + + Please enter a valid date and time. + Zəhmət olmasa doğru bir tarix ve saat daxil edin. + + + Please enter a valid date. + Zəhmət olmasa doğru bir tarix daxil edin. + + + Please select a valid file. + Zəhmət olmasa doğru bir fayl seçin. + + + The hidden field is invalid. + Gizli sahə doğru deyil. + + + Please enter an integer. + Zəhmət olmasa bir tam ədəd daxil edin. + + + Please select a valid language. + Zəhmət olmasa doğru bir dil seçin. + + + Please select a valid locale. + Zəhmət olmasa doğru bir yer seçin. + + + Please enter a valid money amount. + Zəhmət olmasa doğru bir pul miqdarı daxil edin. + + + Please enter a number. + Zəhmət olmasa doğru bir rəqəm daxil edin. + + + The password is invalid. + Parol doğru deyil. + + + Please enter a percentage value. + Zəhmət olmasa doğru bir faiz dəyəri daxil edin. + + + The values do not match. + Dəyərlər örtüşmür. + + + Please enter a valid time. + Zəhmət olmasa doğru bir saat daxil edin. + + + Please select a valid timezone. + Zəhmət olmasa doğru bir saat qurşağı seçin. + + + Please enter a valid URL. + Zəhmət olmasa doğru bir URL daxil edin. + + + Please enter a valid search term. + Zəhmət olmasa doğru bir axtarış termini daxil edin. + + + Please provide a valid phone number. + Zəhmət olmasa doğru bir telefon nömrəsi seçin. + + + The checkbox has an invalid value. + Seçim qutusunda doğru olmayan dəyər var. + + + Please enter a valid email address. + Zəhmət olmasa doğru bir e-poçt seçin. + + + Please select a valid option. + Zəhmət olmasa doğru bir variant seçin. + + + Please select a valid range. + Zəhmət olmasa doğru bir aralıq seçin. + + + Please enter a valid week. + Zəhmət olmasa doğru bir həftə seçin. + diff --git a/src/Symfony/Component/Form/Resources/translations/validators.bs.xlf b/src/Symfony/Component/Form/Resources/translations/validators.bs.xlf index cfe681e3b0534..259b05f842995 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.bs.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.bs.xlf @@ -114,6 +114,26 @@ Please provide a valid phone number. Molim navedite ispravan broj telefona. + + The checkbox has an invalid value. + Polje za potvrdu sadrži neispravnu vrijednost. + + + Please enter a valid email address. + Molim upišite ispravnu email adresu. + + + Please select a valid option. + Molim izaberite ispravnu opciju. + + + Please select a valid range. + Molim izaberite ispravan opseg. + + + Please enter a valid week. + Molim upišite ispravnu sedmicu. + diff --git a/src/Symfony/Component/Form/Resources/translations/validators.ca.xlf b/src/Symfony/Component/Form/Resources/translations/validators.ca.xlf index 3a2fa484f8496..69379608048c9 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.ca.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.ca.xlf @@ -14,6 +14,126 @@ The CSRF token is invalid. Please try to resubmit the form. El token CSRF no és vàlid. Per favor, provi d'enviar novament el formulari. + + This value is not a valid HTML5 color. + Aquest valor no és un color HTML5 valid. + + + Please enter a valid birthdate. + Per favor introdueix una data d'aniversari valida. + + + The selected choice is invalid. + L'opció escollida és invalida. + + + The collection is invalid. + La col·lecció és invalida. + + + Please select a valid color. + Per favor selecciona un color vàlid. + + + Please select a valid country. + Per favor selecciona una ciutat vàlida. + + + Please select a valid currency. + Per favor selecciona una moneda vàlida. + + + Please choose a valid date interval. + Per favor escull un interval de dates vàlides. + + + Please enter a valid date and time. + Per favor introdueix una data i temps vàlid. + + + Please enter a valid date. + Per favor introdueix una data vàlida. + + + Please select a valid file. + Per favor selecciona un arxiu vàlid. + + + The hidden field is invalid. + El camp ocult és invàlid. + + + Please enter an integer. + Per favor introdueix un enter. + + + Please select a valid language. + Per favor selecciona un idioma vàlid. + + + Please select a valid locale. + Per favor seleccioneu una configuració regional vàlida + + + Please enter a valid money amount. + Per favor introdueix una quantitat de diners vàlids. + + + Please enter a number. + Per favor introdueix un número. + + + The password is invalid. + La contrasenya es invàlida. + + + Please enter a percentage value. + Per favor introdueix un valor percentual. + + + The values do not match. + Els valors no coincideixen. + + + Please enter a valid time. + Per favor introdueix un temps vàlid. + + + Please select a valid timezone. + Per favor selecciona una zona horària vàlida. + + + Please enter a valid URL. + Per favor introdueix una URL vàlida. + + + Please enter a valid search term. + Per favor introdueix un concepte de cerca vàlid. + + + Please provide a valid phone number. + Per favor introdueix un número de telèfon vàlid. + + + The checkbox has an invalid value. + La casella de selecció te un valor invàlid. + + + Please enter a valid email address. + Per favor introdueix un correu electrònic vàlid. + + + Please select a valid option. + Per favor selecciona una opció vàlida. + + + Please select a valid range. + Per favor selecciona un rang vàlid. + + + Please enter a valid week. + Per favor introdueix una setmana vàlida. + diff --git a/src/Symfony/Component/Form/Resources/translations/validators.da.xlf b/src/Symfony/Component/Form/Resources/translations/validators.da.xlf index f52f4e0a30db9..dafe20fa02c32 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.da.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.da.xlf @@ -12,7 +12,127 @@ The CSRF token is invalid. Please try to resubmit the form. - CSRF-token er ugyldig. + CSRF-token er ugyldig. Prøv venligst at genindsende. + + + This value is not a valid HTML5 color. + Værdien er ikke en gyldig HTML5 farve. + + + Please enter a valid birthdate. + Indtast venligst en gyldig fødselsdato. + + + The selected choice is invalid. + Den valgte mulighed er ugyldig . + + + The collection is invalid. + Samlingen er ugyldig. + + + Please select a valid color. + Vælg venligst en gyldig farve. + + + Please select a valid country. + Vælg venligst et gyldigt land. + + + Please select a valid currency. + Vælg venligst en gyldig valuta. + + + Please choose a valid date interval. + Vælg venligst et gyldigt datointerval. + + + Please enter a valid date and time. + Vælg venligst en gyldig dato og tid. + + + Please enter a valid date. + Vælg venligst en gyldig dato. + + + Please select a valid file. + Vælg venligst en gyldig fil. + + + The hidden field is invalid. + Det skjulte felt er ugyldigt. + + + Please enter an integer. + Indsæt veligst et heltal. + + + Please select a valid language. + Vælg venligst et gyldigt sprog. + + + Please select a valid locale. + Vælg venligst en gyldigt sprogkode. + + + Please enter a valid money amount. + Vælg venligst et gyldigt beløb. + + + Please enter a number. + Indtast venligst et nummer. + + + The password is invalid. + Passwordet er ugyldigt. + + + Please enter a percentage value. + Indtast venligst en procentværdi. + + + The values do not match. + Værdierne er ikke ens. + + + Please enter a valid time. + Indtast venligst en gyldig tid. + + + Please select a valid timezone. + Vælg venligst en gyldig tidszone. + + + Please enter a valid URL. + Indtast venligst en gyldig URL. + + + Please enter a valid search term. + Indtast venligst et gyldigt søgeord. + + + Please provide a valid phone number. + Giv venligst et gyldigt telefonnummer. + + + The checkbox has an invalid value. + Checkboxen har en ugyldigt værdi. + + + Please enter a valid email address. + Indtast venligst en gyldig emailaddresse. + + + Please select a valid option. + Vælg venligst en gyldig mulighed. + + + Please select a valid range. + Vælg venligst et gyldigt interval . + + + Please enter a valid week. + Indtast venligst en gyldig uge. diff --git a/src/Symfony/Component/Form/Resources/translations/validators.el.xlf b/src/Symfony/Component/Form/Resources/translations/validators.el.xlf index ba2ced74b0111..595630e76f453 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.el.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.el.xlf @@ -14,6 +14,126 @@ The CSRF token is invalid. Please try to resubmit the form. Το CSRF token δεν είναι έγκυρο. Παρακαλούμε δοκιμάστε να υποβάλετε τη φόρμα ξανά. + + This value is not a valid HTML5 color. + Αυτή η τιμή δέν έναι έγκυρο χρώμα HTML5. + + + Please enter a valid birthdate. + Παρακαλόυμε ειχάγεται μία έγκυρη ημερομηνία γέννησης. + + + The selected choice is invalid. + Η επιλεγμένη επιλογή δέν είναι έγκυρη. + + + The collection is invalid. + Η συλλογή δέν είναι έγκυρη. + + + Please select a valid color. + Παρακαλούμε επιλέξτε ένα έγκυρο χρώμα. + + + Please select a valid country. + Παρακαλούμε επιλέξτε μία έγκυρη χώρα. + + + Please select a valid currency. + Παρακαλούμε επιλέξτε ένα έγυρο νόμισμα. + + + Please choose a valid date interval. + Παρακαλούμε επιλέξτε ένα έγκυρο διάστημα ημερομηνίας. + + + Please enter a valid date and time. + Παρακαλούμε εισαγάγετε μια έγκυρη ημερομηνία και ώρα. + + + Please enter a valid date. + Παρακαλούμε εισάγετε μία έγκυρη ημερομηνία. + + + Please select a valid file. + Παρακαλούμε επιλέξτε ένα έγκυρο αρχείο. + + + The hidden field is invalid. + Το κρυφό πεδίο δέν είναι έγκυρο. + + + Please enter an integer. + Παρακαλούμε εισάγετε έναν ακέραιο αριθμό. + + + Please select a valid language. + Παρακαλούμε επιλέξτε μία έγκυρη γλώσσα. + + + Please select a valid locale. + Παρακαλούμε επιλέξτε μία έγκυρη τοπικοποίηση. + + + Please enter a valid money amount. + Παρακαλούμε εισάγετε ένα έγκυρο χρηματικό ποσό. + + + Please enter a number. + Παρακαλούμε εισάγετε έναν αριθμό. + + + The password is invalid. + Ο κωδικός δέν είναι έγκυρος. + + + Please enter a percentage value. + Παρακαλούμε εισάγετε μία ποσοστιαία τιμή. + + + The values do not match. + Οι τιμές δέν ταιριάζουν. + + + Please enter a valid time. + Παρακαλούμε εισάγετε μία έγκυρη ώρα. + + + Please select a valid timezone. + Παρακαλούμε επιλέξτε μία έγυρη ζώνη ώρας. + + + Please enter a valid URL. + Παρακαλούμε εισάγετε μια έγκυρη διεύθυνση URL. + + + Please enter a valid search term. + Παρακαλούμε εισάγετε έναν έγκυρο όρο αναζήτησης. + + + Please provide a valid phone number. + Παρακαλούμε καταχωρίστε έναν έγκυρο αριθμό τηλεφώνου. + + + The checkbox has an invalid value. + Το πλαίσιο ελέγχου έχει μή έγκυρη τιμή. + + + Please enter a valid email address. + Παρακαλούμε εισάγετε μία έγκυρη ηλεκτρονική διεύθυνση. + + + Please select a valid option. + Παρακαλούμε επιλέξτε μία έγκυρη επιλογή. + + + Please select a valid range. + Παρακαλούμε επιλέξτε ένα έγυρο εύρος. + + + Please enter a valid week. + Παρακαλούμε εισάγετε μία έγκυρη εβδομάδα. + diff --git a/src/Symfony/Component/Form/Resources/translations/validators.fi.xlf b/src/Symfony/Component/Form/Resources/translations/validators.fi.xlf index 4eb393aee90b1..7ad87b5468261 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.fi.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.fi.xlf @@ -3,8 +3,8 @@ - This field group should not contain extra fields. - Tämä kenttäryhmä ei voi sisältää ylimääräisiä kenttiä. + This form should not contain extra fields. + Tämä lomake ei voi sisältää ylimääräisiä kenttiä. The uploaded file was too large. Please try to upload a smaller file. @@ -12,7 +12,127 @@ The CSRF token is invalid. Please try to resubmit the form. - CSRF tarkiste on virheellinen. Ole hyvä ja yritä lähettää lomake uudestaan. + CSRF-tarkiste on virheellinen. Ole hyvä ja yritä lähettää lomake uudestaan. + + + This value is not a valid HTML5 color. + Tämä arvo ei ole kelvollinen HTML5-väri. + + + Please enter a valid birthdate. + Syötä kelvollinen syntymäaika. + + + The selected choice is invalid. + Valittu vaihtoehto ei kelpaa. + + + The collection is invalid. + Ryhmä ei kelpaa. + + + Please select a valid color. + Valitse kelvollinen väri. + + + Please select a valid country. + Valitse kelvollinen maa. + + + Please select a valid currency. + Valitse kelvollinen valuutta. + + + Please choose a valid date interval. + Valitse kelvollinen aikaväli. + + + Please enter a valid date and time. + Syötä kelvolliset päivä ja aika. + + + Please enter a valid date. + Syötä kelvollinen päivä. + + + Please select a valid file. + Valitse kelvollinen tiedosto. + + + The hidden field is invalid. + Piilotettu kenttä ei ole kelvollinen. + + + Please enter an integer. + Syötä kokonaisluku. + + + Please select a valid language. + Valitse kelvollinen kieli. + + + Please select a valid locale. + Valitse kelvollinen kielikoodi. + + + Please enter a valid money amount. + Syötä kelvollinen rahasumma. + + + Please enter a number. + Syötä numero. + + + The password is invalid. + Salasana ei kelpaa. + + + Please enter a percentage value. + Syötä prosenttiluku. + + + The values do not match. + Arvot eivät vastaa toisiaan. + + + Please enter a valid time. + Syötä kelvollinen kellonaika. + + + Please select a valid timezone. + Valitse kelvollinen aikavyöhyke. + + + Please enter a valid URL. + Syötä kelvollinen URL. + + + Please enter a valid search term. + Syötä kelvollinen hakusana. + + + Please provide a valid phone number. + Anna kelvollinen puhelinnumero. + + + The checkbox has an invalid value. + Valintaruudun arvo ei kelpaa. + + + Please enter a valid email address. + Syötä kelvollinen sähköpostiosoite. + + + Please select a valid option. + Valitse kelvollinen vaihtoehto. + + + Please select a valid range. + Valitse kelvollinen väli. + + + Please enter a valid week. + Syötä kelvollinen viikko. diff --git a/src/Symfony/Component/Form/Resources/translations/validators.gl.xlf b/src/Symfony/Component/Form/Resources/translations/validators.gl.xlf index 88cc6536ead7f..5ef404a481a45 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.gl.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.gl.xlf @@ -14,6 +14,126 @@ The CSRF token is invalid. Please try to resubmit the form. O token CSRF non é válido. Por favor, probe a enviar novamente o formulario. + + This value is not a valid HTML5 color. + Este valor non é unha cor HTML5 válida. + + + Please enter a valid birthdate. + Insire unha data de aniversario válida. + + + The selected choice is invalid. + A opción seleccionada non é válida. + + + The collection is invalid. + A colección non é válida. + + + Please select a valid color. + Por favor, seleccione unha cor válida. + + + Please select a valid country. + Por favor, seleccione un país válido. + + + Please select a valid currency. + Por favor, seleccione unha moeda válida. + + + Please choose a valid date interval. + Por favor, escolla un intervalo de datas válido. + + + Please enter a valid date and time. + Por favor, introduza unha data e hora válidas. + + + Please enter a valid date. + Por favor, introduce unha data válida. + + + Please select a valid file. + Por favor, seleccione un ficheiro válido. + + + The hidden field is invalid. + O campo oculto non é válido. + + + Please enter an integer. + Por favor, introduza un número enteiro. + + + Please select a valid language. + Por favor, selecciona un idioma válido. + + + Please select a valid locale. + Por favor, seleccione unha configuración rexional válida. + + + Please enter a valid money amount. + Por favor, introduza unha cantidade de diñeiro válida. + + + Please enter a number. + Por favor, introduza un número. + + + The password is invalid. + O contrasinal non é válido. + + + Please enter a percentage value. + Por favor, introduza un valor porcentual. + + + The values do not match. + Os valores non coinciden. + + + Please enter a valid time. + Por favor, introduza unha hora válida. + + + Please select a valid timezone. + Por favor, selecciona unha zona horaria válida. + + + Please enter a valid URL. + Por favor, introduce un URL válido. + + + Please enter a valid search term. + Por favor, introduce un termo de busca válido. + + + Please provide a valid phone number. + Por favor, fornecer un número de teléfono válido. + + + The checkbox has an invalid value. + A caixa de verificación ten un valor non válido. + + + Please enter a valid email address. + Por favor, introduce un enderezo de correo electrónico válido. + + + Please select a valid option. + Por favor, seleccione unha opción válida. + + + Please select a valid range. + Por favor, seleccione un intervalo válido. + + + Please enter a valid week. + Por favor, introduce unha semana válida. + diff --git a/src/Symfony/Component/Form/Resources/translations/validators.he.xlf b/src/Symfony/Component/Form/Resources/translations/validators.he.xlf index 74a71d05a31a8..efd68b8807bfd 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.he.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.he.xlf @@ -14,6 +14,126 @@ The CSRF token is invalid. Please try to resubmit the form. אסימון CSRF אינו חוקי. אנא נסה לשלוח שוב את הטופס. + + This value is not a valid HTML5 color. + ערך זה אינו צבע HTML5 חוקי. + + + Please enter a valid birthdate. + נא להזין את תאריך לידה תקני. + + + The selected choice is invalid. + הבחירה שנבחרה אינה חוקית. + + + The collection is invalid. + האוסף אינו חוקי. + + + Please select a valid color. + אנא בחר צבע חוקי. + + + Please select a valid country. + אנא בחר מדינה חוקית. + + + Please select a valid currency. + אנא בחר מטבע חוקי. + + + Please choose a valid date interval. + אנא בחר מרווח תאריכים חוקי. + + + Please enter a valid date and time. + אנא הזן תאריך ושעה תקנים. + + + Please enter a valid date. + נא להזין תאריך חוקי. + + + Please select a valid file. + אנא בחר קובץ חוקי. + + + The hidden field is invalid. + השדה הנסתר אינו חוקי. + + + Please enter an integer. + אנא הזן מספר שלם. + + + Please select a valid language. + אנא בחר שפה חוקי. + + + Please select a valid locale. + אנא בחר שפה מקומית. + + + Please enter a valid money amount. + אנא הזן סכום כסף חוקי. + + + Please enter a number. + אנא הזן מספר. + + + The password is invalid. + הסיסמה אינה חוקית. + + + Please enter a percentage value. + אנא הזן ערך באחוזים. + + + The values do not match. + הערכים אינם תואמים. + + + Please enter a valid time. + אנא הזן שעה חוקי. + + + Please select a valid timezone. + אנא בחר אזור זמן חוקי. + + + Please enter a valid URL. + נא להזין את כתובת אתר חוקית. + + + Please enter a valid search term. + אנא הזן מונח חיפוש חוקי. + + + Please provide a valid phone number. + אנא ספק מספר טלפון חוקי. + + + The checkbox has an invalid value. + לתיבת הסימון יש ערך לא חוקי. + + + Please enter a valid email address. + אנא הזן כתובת דוא"ל תקנית. + + + Please select a valid option. + אנא בחר אפשרות חוקית. + + + Please select a valid range. + אנא בחר טווח חוקי. + + + Please enter a valid week. + אנא הזן שבוע תקף. + diff --git a/src/Symfony/Component/Form/Resources/translations/validators.hu.xlf b/src/Symfony/Component/Form/Resources/translations/validators.hu.xlf index bc7f6055e4845..3b70461d394b7 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.hu.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.hu.xlf @@ -18,6 +18,122 @@ This value is not a valid HTML5 color. Ez az érték nem egy érvényes HTML5 szín. + + Please enter a valid birthdate. + Kérjük, adjon meg egy valós születési dátumot. + + + The selected choice is invalid. + A kiválasztott opció érvénytelen. + + + The collection is invalid. + A gyűjtemény érvénytelen. + + + Please select a valid color. + Kérjük, válasszon egy érvényes színt. + + + Please select a valid country. + Kérjük, válasszon egy érvényes országot. + + + Please select a valid currency. + Kérjük, válasszon egy érvényes pénznemet. + + + Please choose a valid date interval. + Kérjük, válasszon egy érvényes dátumintervallumot. + + + Please enter a valid date and time. + Kérjük, adjon meg egy érvényes dátumot és időpontot. + + + Please enter a valid date. + Kérjük, adjon meg egy érvényes dátumot. + + + Please select a valid file. + Kérjük, válasszon egy érvényes fájlt. + + + The hidden field is invalid. + A rejtett mező érvénytelen. + + + Please enter an integer. + Kérjük, adjon meg egy egész számot. + + + Please select a valid language. + Kérjük, válasszon egy érvényes nyelvet. + + + Please select a valid locale. + Kérjük, válasszon egy érvényes területi beállítást. + + + Please enter a valid money amount. + Kérjük, adjon meg egy érvényes pénzösszeget. + + + Please enter a number. + Kérjük, adjon meg egy számot. + + + The password is invalid. + A jelszó érvénytelen. + + + Please enter a percentage value. + Kérjük, adjon meg egy százalékos értéket. + + + The values do not match. + Az értékek nem egyeznek. + + + Please enter a valid time. + Kérjük, adjon meg egy érvényes időpontot. + + + Please select a valid timezone. + Kérjük, válasszon érvényes időzónát. + + + Please enter a valid URL. + Kérjük, adjon meg egy érvényes URL-t. + + + Please enter a valid search term. + Kérjük, adjon meg egy érvényes keresési kifejezést. + + + Please provide a valid phone number. + Kérjük, adjon egy érvényes telefonszámot + + + The checkbox has an invalid value. + A jelölőnégyzet értéke érvénytelen. + + + Please enter a valid email address. + Kérjük valós e-mail címet adjon meg. + + + Please select a valid option. + Kérjük, válasszon egy érvényes beállítást. + + + Please select a valid range. + Kérjük, válasszon egy érvényes tartományt. + + + Please enter a valid week. + Kérjük, adjon meg egy érvényes hetet. + diff --git a/src/Symfony/Component/Form/Resources/translations/validators.lt.xlf b/src/Symfony/Component/Form/Resources/translations/validators.lt.xlf index e9eebc7389739..5613c42b5bf16 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.lt.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.lt.xlf @@ -14,6 +14,126 @@ The CSRF token is invalid. Please try to resubmit the form. CSRF kodas nepriimtinas. Bandykite siųsti formos užklausą dar kartą. + + This value is not a valid HTML5 color. + Ši reikšmė nėra HTML5 spalva. + + + Please enter a valid birthdate. + Prašome įvesti tinkamą gimimo datą. + + + The selected choice is invalid. + Pasirinktas pasirinkimas yra neteisingas. + + + The collection is invalid. + Neteisingas sąrašas. + + + Please select a valid color. + Prašome pasirinkti tinkamą spalvą. + + + Please select a valid country. + Prašome pasirinkti tinkamą šalį. + + + Please select a valid currency. + Prašome pasirinkti tinkamą valiutą. + + + Please choose a valid date interval. + Prašome pasirinkti tinkamą datos intervalą. + + + Please enter a valid date and time. + Prašome įvesti tinkamą datą ir laiką. + + + Please enter a valid date. + Prašome įvesti tinkamą datą. + + + Please select a valid file. + Prašome pasirinkti tinkamą bylą. + + + The hidden field is invalid. + Klaidingas paslėptasis laukas. + + + Please enter an integer. + Prašome įvesti sveiką skaičių. + + + Please select a valid language. + Prašome pasirinkti tinkamą kalbą. + + + Please select a valid locale. + Prašome pasirinkti tinkamą lokalę. + + + Please enter a valid money amount. + Prašome įvesti tinkamą pinigų sumą. + + + Please enter a number. + Prašome įvesti numerį. + + + The password is invalid. + Klaidingas slaptažodis. + + + Please enter a percentage value. + Prašome įvesti procentinę reikšmę. + + + The values do not match. + Reikšmės nesutampa. + + + Please enter a valid time. + Prašome įvesti tinkamą laiką. + + + Please select a valid timezone. + Prašome pasirinkti tinkamą laiko zoną. + + + Please enter a valid URL. + Prašome įvesti tinkamą URL. + + + Please enter a valid search term. + Prašome įvesti tinkamą paieškos terminą. + + + Please provide a valid phone number. + Prašome pateikti tinkamą telefono numerį. + + + The checkbox has an invalid value. + Klaidinga žymimajo langelio reikšmė. + + + Please enter a valid email address. + Prašome įvesti tinkamą el. pašto adresą. + + + Please select a valid option. + Prašome pasirinkti tinkamą parinktį. + + + Please select a valid range. + Prašome pasirinkti tinkamą diapozoną. + + + Please enter a valid week. + Prašome įvesti tinkamą savaitę. + diff --git a/src/Symfony/Component/Form/Resources/translations/validators.mn.xlf b/src/Symfony/Component/Form/Resources/translations/validators.mn.xlf index 171df74d63c0c..620112d8814a9 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.mn.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.mn.xlf @@ -14,6 +14,126 @@ The CSRF token is invalid. Please try to resubmit the form. CSRF token буруу байна. Формоо дахин илгээнэ үү. + + This value is not a valid HTML5 color. + Энэ утга зөв HTML5 өнгө биш байна. + + + Please enter a valid birthdate. + Зөв төрсөн он сар оруулна уу. + + + The selected choice is invalid. + Сонгосон утга буруу байна. + + + The collection is invalid. + Цуглуулга буруу байна. + + + Please select a valid color. + Үнэн зөв өнгө сонгоно уу. + + + Please select a valid country. + Үнэн зөв улс сонгоно уу. + + + Please select a valid currency. + Үнэн зөв мөнгөн тэмдэгт сонгоно уу. + + + Please choose a valid date interval. + Үнэн зөв цагын зай сонгоно уу. + + + Please enter a valid date and time. + Үнэн зөв он цаг оруулна уу. + + + Please enter a valid date. + Үнэн зөв он цаг өдөр оруулна уу. + + + Please select a valid file. + Үнэн зөв файл сонгоно уу. + + + The hidden field is invalid. + Нууц талбарын утга буруу байна. + + + Please enter an integer. + Бүхэл тоо оруулна уу. + + + Please select a valid language. + Үнэн зөв хэл сонгоно уу. + + + Please select a valid locale. + Үнэн зөв бүс сонгоно уу. + + + Please enter a valid money amount. + Үнэн зөв мөнгөний хэмжээ сонгоно уу. + + + Please enter a number. + Тоо оруулна уу. + + + The password is invalid. + Нууц үг буруу байна. + + + Please enter a percentage value. + Хувь утга оруулна уу. + + + The values do not match. + Утга хоорондоо таарахгүй байна. + + + Please enter a valid time. + Үнэн зөв цаг оруулна уу. + + + Please select a valid timezone. + Үнэн зөв цагын бүс оруулна уу. + + + Please enter a valid URL. + Үнэн зөв URL оруулна уу. + + + Please enter a valid search term. + Үнэн зөв хайх утга оруулна уу. + + + Please provide a valid phone number. + Үнэн зөв утасны дугаар оруулна уу. + + + The checkbox has an invalid value. + Сонгох хайрцаг буруу утгатай байна. + + + Please enter a valid email address. + Үнэн зөв и-мэйл хаяг оруулна уу. + + + Please select a valid option. + Үнэн зөв сонголт сонгоно уу. + + + Please select a valid range. + Үнэн зөв хязгаарын утга сонгоно уу. + + + Please enter a valid week. + Үнэн зөв долоо хоног сонгоно уу. + diff --git a/src/Symfony/Component/Form/Resources/translations/validators.nb.xlf b/src/Symfony/Component/Form/Resources/translations/validators.nb.xlf index c64266c99189b..1d8385086aa82 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.nb.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.nb.xlf @@ -14,6 +14,126 @@ The CSRF token is invalid. CSRF nøkkelen er ugyldig. + + This value is not a valid HTML5 color. + Denne verdien er ikke en gyldig HTML5-farge. + + + Please enter a valid birthdate. + Vennligst oppgi gyldig fødselsdato. + + + The selected choice is invalid. + Det valgte valget er ugyldig. + + + The collection is invalid. + Samlingen er ugyldig. + + + Please select a valid color. + Velg en gyldig farge. + + + Please select a valid country. + Vennligst velg et gyldig land. + + + Please select a valid currency. + Vennligst velg en gyldig valuta. + + + Please choose a valid date interval. + Vennligst velg et gyldig datointervall. + + + Please enter a valid date and time. + Vennligst angi en gyldig dato og tid. + + + Please enter a valid date. + Vennligst oppgi en gyldig dato. + + + Please select a valid file. + Vennligst velg en gyldig fil. + + + The hidden field is invalid. + Det skjulte feltet er ugyldig. + + + Please enter an integer. + Vennligst skriv inn et heltall. + + + Please select a valid language. + Vennligst velg et gyldig språk. + + + Please select a valid locale. + Vennligst velg et gyldig sted. + + + Please enter a valid money amount. + Vennligst angi et gyldig pengebeløp. + + + Please enter a number. + Vennligst skriv inn et nummer. + + + The password is invalid. + Passordet er ugyldig. + + + Please enter a percentage value. + Vennligst angi en prosentverdi. + + + The values do not match. + Verdiene stemmer ikke overens. + + + Please enter a valid time. + Vennligst angi et gyldig tidspunkt. + + + Please select a valid timezone. + Vennligst velg en gyldig tidssone. + + + Please enter a valid URL. + Vennligst skriv inn en gyldig URL. + + + Please enter a valid search term. + Vennligst angi et gyldig søketerm. + + + Please provide a valid phone number. + Vennligst oppgi et gyldig telefonnummer. + + + The checkbox has an invalid value. + Avkrysningsboksen har en ugyldig verdi. + + + Please enter a valid email address. + Vennligst skriv inn en gyldig e-post adresse. + + + Please select a valid option. + Vennligst velg et gyldig alternativ. + + + Please select a valid range. + Vennligst velg et gyldig område. + + + Please enter a valid week. + Vennligst skriv inn en gyldig uke. + diff --git a/src/Symfony/Component/Form/Resources/translations/validators.no.xlf b/src/Symfony/Component/Form/Resources/translations/validators.no.xlf index c64266c99189b..1d8385086aa82 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.no.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.no.xlf @@ -14,6 +14,126 @@ The CSRF token is invalid. CSRF nøkkelen er ugyldig. + + This value is not a valid HTML5 color. + Denne verdien er ikke en gyldig HTML5-farge. + + + Please enter a valid birthdate. + Vennligst oppgi gyldig fødselsdato. + + + The selected choice is invalid. + Det valgte valget er ugyldig. + + + The collection is invalid. + Samlingen er ugyldig. + + + Please select a valid color. + Velg en gyldig farge. + + + Please select a valid country. + Vennligst velg et gyldig land. + + + Please select a valid currency. + Vennligst velg en gyldig valuta. + + + Please choose a valid date interval. + Vennligst velg et gyldig datointervall. + + + Please enter a valid date and time. + Vennligst angi en gyldig dato og tid. + + + Please enter a valid date. + Vennligst oppgi en gyldig dato. + + + Please select a valid file. + Vennligst velg en gyldig fil. + + + The hidden field is invalid. + Det skjulte feltet er ugyldig. + + + Please enter an integer. + Vennligst skriv inn et heltall. + + + Please select a valid language. + Vennligst velg et gyldig språk. + + + Please select a valid locale. + Vennligst velg et gyldig sted. + + + Please enter a valid money amount. + Vennligst angi et gyldig pengebeløp. + + + Please enter a number. + Vennligst skriv inn et nummer. + + + The password is invalid. + Passordet er ugyldig. + + + Please enter a percentage value. + Vennligst angi en prosentverdi. + + + The values do not match. + Verdiene stemmer ikke overens. + + + Please enter a valid time. + Vennligst angi et gyldig tidspunkt. + + + Please select a valid timezone. + Vennligst velg en gyldig tidssone. + + + Please enter a valid URL. + Vennligst skriv inn en gyldig URL. + + + Please enter a valid search term. + Vennligst angi et gyldig søketerm. + + + Please provide a valid phone number. + Vennligst oppgi et gyldig telefonnummer. + + + The checkbox has an invalid value. + Avkrysningsboksen har en ugyldig verdi. + + + Please enter a valid email address. + Vennligst skriv inn en gyldig e-post adresse. + + + Please select a valid option. + Vennligst velg et gyldig alternativ. + + + Please select a valid range. + Vennligst velg et gyldig område. + + + Please enter a valid week. + Vennligst skriv inn en gyldig uke. + diff --git a/src/Symfony/Component/Form/Resources/translations/validators.pt.xlf b/src/Symfony/Component/Form/Resources/translations/validators.pt.xlf index 554a810c68673..6ce1c3242cab3 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.pt.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.pt.xlf @@ -4,15 +4,135 @@ This form should not contain extra fields. - Este formulário não deveria conter campos extra. + Este formulário não deveria possuir mais campos. The uploaded file was too large. Please try to upload a smaller file. - O arquivo enviado é muito grande. Por favor, tente enviar um ficheiro mais pequeno. + O ficheiro enviado é muito grande. Por favor, tente enviar um ficheiro menor. The CSRF token is invalid. Please try to resubmit the form. - O token CSRF é inválido. Por favor submeta o formulário novamente. + O token CSRF está inválido. Por favor, tente enviar o formulário novamente. + + + This value is not a valid HTML5 color. + Este valor não é uma cor HTML5 válida. + + + Please enter a valid birthdate. + Por favor, informe uma data de nascimento válida. + + + The selected choice is invalid. + A escolha seleccionada é inválida. + + + The collection is invalid. + A coleção é inválida. + + + Please select a valid color. + Por favor, selecione uma cor válida. + + + Please select a valid country. + Por favor, selecione um país válido. + + + Please select a valid currency. + Por favor, selecione uma moeda válida. + + + Please choose a valid date interval. + Por favor, escolha um intervalo de datas válido. + + + Please enter a valid date and time. + Por favor, informe uma data e horário válidos. + + + Please enter a valid date. + Por favor, informe uma data válida. + + + Please select a valid file. + Por favor, selecione um ficheiro válido. + + + The hidden field is invalid. + O campo oculto é inválido. + + + Please enter an integer. + Por favor, informe um inteiro. + + + Please select a valid language. + Por favor selecione um idioma válido. + + + Please select a valid locale. + Por favor, selecione um locale válido. + + + Please enter a valid money amount. + Por favor, informe um valor monetário válido. + + + Please enter a number. + Por favor, informe um número. + + + The password is invalid. + A palavra-passe é inválida. + + + Please enter a percentage value. + Por favor, informe um valor percentual. + + + The values do not match. + Os valores não correspondem. + + + Please enter a valid time. + Por favor, informe uma hora válida. + + + Please select a valid timezone. + Por favor, selecione um fuso horário válido. + + + Please enter a valid URL. + Por favor, informe uma URL válida. + + + Please enter a valid search term. + Por favor, informe um termo de busca válido. + + + Please provide a valid phone number. + Por favor, infome um número de telefone válido. + + + The checkbox has an invalid value. + O checkbox possui um valor inválido. + + + Please enter a valid email address. + Por favor, informe um endereço de email válido. + + + Please select a valid option. + Por favor, selecione uma opção válida. + + + Please select a valid range. + Por favor, selecione um intervalo válido. + + + Please enter a valid week. + Por favor, selecione uma semana válida. diff --git a/src/Symfony/Component/Form/Resources/translations/validators.ro.xlf b/src/Symfony/Component/Form/Resources/translations/validators.ro.xlf index 25abab3b6f148..a7dc62b579c6b 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.ro.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.ro.xlf @@ -4,7 +4,7 @@ This form should not contain extra fields. - Aceast formular nu ar trebui să conțină câmpuri suplimentare. + Acest formular nu ar trebui să conțină câmpuri suplimentare. The uploaded file was too large. Please try to upload a smaller file. @@ -12,8 +12,128 @@ The CSRF token is invalid. Please try to resubmit the form. - Token-ul CSRF este invalid. Vă rugăm să trimiteți formularul incă o dată. + Token-ul CSRF este invalid. Vă rugăm să retrimiteți formularul. + + + This value is not a valid HTML5 color. + Această valoare nu este un cod de culoare HTML5 valid. + + + Please enter a valid birthdate. + Vă rugăm să introduceți o dată de naștere validă. + + + The selected choice is invalid. + Valoarea selectată este invalidă. + + + The collection is invalid. + Colecția nu este validă. + + + Please select a valid color. + Vă rugăm să selectați o culoare validă. + + + Please select a valid country. + Vă rugăm să selectați o țară validă. + + + Please select a valid currency. + Vă rugăm să selectați o monedă validă. + + + Please choose a valid date interval. + Vă rugăm să selectați un interval de zile valid. + + + Please enter a valid date and time. + Vă rugăm să introduceți o dată și o oră validă. + + + Please enter a valid date. + Vă rugăm să introduceți o dată validă. + + + Please select a valid file. + Vă rugăm să selectați un fișier valid. + + + The hidden field is invalid. + Câmpul ascuns este invalid. + + + Please enter an integer. + Vă rugăm să introduceți un număr întreg. + + + Please select a valid language. + Vă rugăm să selectați o limbă validă. + + + Please select a valid locale. + Vă rugăm să selectați o setare locală validă. + + + Please enter a valid money amount. + Vă rugăm să introduceți o valoare monetară corectă. + + + Please enter a number. + Vă rugăm să introduceți un număr. + + + The password is invalid. + Parola nu este validă. + + + Please enter a percentage value. + Vă rugăm să introduceți o valoare procentuală. + + + The values do not match. + Valorile nu coincid. + + + Please enter a valid time. + Vă rugăm să introduceți o oră validă. + + + Please select a valid timezone. + Vă rugăm să selectați un fus orar valid. + + + Please enter a valid URL. + Vă rugăm să introduceți un URL valid. + + + Please enter a valid search term. + Vă rugăm să introduceți un termen de căutare valid. + + + Please provide a valid phone number. + Vă rugăm să introduceți un număr de telefon valid. + + + The checkbox has an invalid value. + Bifa nu are o valoare validă. + + + Please enter a valid email address. + Vă rugăm să introduceți o adresă de email validă. + + + Please select a valid option. + Vă rugăm să selectați o opțiune validă. + + + Please select a valid range. + Vă rugăm să selectați un interval valid. + + + Please enter a valid week. + Vă rugăm să introduceți o săptămână validă. - \ No newline at end of file + diff --git a/src/Symfony/Component/Form/Resources/translations/validators.sk.xlf b/src/Symfony/Component/Form/Resources/translations/validators.sk.xlf index 638d0cc4a0b3e..06b2bbdbead5f 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.sk.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.sk.xlf @@ -14,6 +14,126 @@ The CSRF token is invalid. Please try to resubmit the form. CSRF token je neplatný. Prosím skúste znovu odoslať formulár. + + This value is not a valid HTML5 color. + Táto hodnota nie je platná HTML5 farba. + + + Please enter a valid birthdate. + Prosím zadajte platný dátum narodenia. + + + The selected choice is invalid. + Vybraná možnosť je neplatná. + + + The collection is invalid. + Kolekcia je neplatná. + + + Please select a valid color. + Prosím vyberte platnú farbu. + + + Please select a valid country. + Prosím vyberte platnú krajinu. + + + Please select a valid currency. + Prosím vyberte platnú menu. + + + Please choose a valid date interval. + Prosím vyberte platný rozsah dát. + + + Please enter a valid date and time. + Prosím zadajte platný dátum a čas. + + + Please enter a valid date. + Prosím zadajte platný dátum. + + + Please select a valid file. + Prosím vyberte platný súbor. + + + The hidden field is invalid. + Skryté pole je neplatné. + + + Please enter an integer. + Prosím zadajte celé číslo. + + + Please select a valid language. + Prosím vyberte platný jazyk. + + + Please select a valid locale. + Prosím vyberte platné miestne nastavenia. + + + Please enter a valid money amount. + Prosím zadajte platnú čiastku. + + + Please enter a number. + Prosím zadajte číslo. + + + The password is invalid. + Heslo je neprávne. + + + Please enter a percentage value. + Prosím zadajte percentuálnu hodnotu. + + + The values do not match. + Hodnoty nie sú zhodné. + + + Please enter a valid time. + Prosím zadajte platný čas. + + + Please select a valid timezone. + Prosím vyberte platné časové pásmo. + + + Please enter a valid URL. + Prosím zadajte platnú URL. + + + Please enter a valid search term. + Prosím zadajte platný vyhľadávací výraz. + + + Please provide a valid phone number. + Prosím zadajte platné telefónne číslo. + + + The checkbox has an invalid value. + Zaškrtávacie políčko má neplatnú hodnotu. + + + Please enter a valid email address. + Prosím zadajte platnú emailovú adresu. + + + Please select a valid option. + Prosím vyberte platnú možnosť. + + + Please select a valid range. + Prosím vyberte platný rozsah. + + + Please enter a valid week. + Prosím zadajte platný týždeň. + diff --git a/src/Symfony/Component/Form/Resources/translations/validators.sr_Cyrl.xlf b/src/Symfony/Component/Form/Resources/translations/validators.sr_Cyrl.xlf index 1aa242f4186a4..a5610e0ead295 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.sr_Cyrl.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.sr_Cyrl.xlf @@ -114,6 +114,26 @@ Please provide a valid phone number. Молим наведите исправан број телефона. + + The checkbox has an invalid value. + Поље за потврду садржи неисправну вредност. + + + Please enter a valid email address. + Молим упишите исправну email адресу. + + + Please select a valid option. + Молим изаберите исправну опцију. + + + Please select a valid range. + Молим изаберите исправан опсег. + + + Please enter a valid week. + Молим упишите исправну седмицу. + - \ No newline at end of file + diff --git a/src/Symfony/Component/Form/Resources/translations/validators.sr_Latn.xlf b/src/Symfony/Component/Form/Resources/translations/validators.sr_Latn.xlf index 75a2aaab70e2e..02fb5aa56ead4 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.sr_Latn.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.sr_Latn.xlf @@ -114,6 +114,26 @@ Please provide a valid phone number. Molim navedite ispravan broj telefona. + + The checkbox has an invalid value. + Polje za potvrdu sadrži neispravnu vrednost. + + + Please enter a valid email address. + Molim upišite ispravnu email adresu. + + + Please select a valid option. + Molim izaberite ispravnu opciju. + + + Please select a valid range. + Molim izaberite ispravan opseg. + + + Please enter a valid week. + Molim upišite ispravnu sedmicu. + - \ No newline at end of file + diff --git a/src/Symfony/Component/Form/Resources/translations/validators.th.xlf b/src/Symfony/Component/Form/Resources/translations/validators.th.xlf new file mode 100644 index 0000000000000..060dc9ec48094 --- /dev/null +++ b/src/Symfony/Component/Form/Resources/translations/validators.th.xlf @@ -0,0 +1,139 @@ + + + + + + This form should not contain extra fields. + ฟอร์มนี้ไม่ควรมี 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 token ไม่ถูกต้อง กรุณาลองส่งแบบฟอร์มใหม่ + + + This value is not a valid HTML5 color. + ค่านี้ไม่ใช่ค่าที่ถูกต้องของค่าสี HTML5 + + + Please enter a valid birthdate. + กรุณากรอกวันเดือนปีเกิดที่ถูกต้อง + + + The selected choice is invalid. + ตัวเลือกที่เลิอกไม่ถูกต้อง + + + The collection is invalid. + คอเล็กชั่นไม่ถูกต้อง + + + Please select a valid color. + กรุณาเลือกค่าสีที่ถูกต้อง + + + Please select a valid country. + กรุณาเลือกประเทศที่ถูกต้อง + + + Please select a valid currency. + กรุุณาเลิอกค่าสกุลเงินที่ถูกต้อง + + + Please choose a valid date interval. + กรุณณากรอกช่วงวันที่ที่ถูกต้อง + + + Please enter a valid date and time. + กรุณณากรอกค่าเวลาและวันที่ที่ถูกต้อง + + + Please enter a valid date. + กรุณณากรอกค่าวันที่ที่ถูกต้อง + + + Please select a valid file. + กรุณาเลือกไฟล์ที่ถูกต้อง + + + The hidden field is invalid. + ค่า Hidden field ไม่ถูกต้อง + + + Please enter an integer. + กรุณากรอกตัวเลขจำนวนเต็ม + + + Please select a valid language. + กรุณาเลือกภาษาที่ถูกต้อง + + + Please select a valid locale. + กรุณาเลือกท้องถิ่นที่ถูกต้อง + + + Please enter a valid money amount. + กรุณากรอกจำนวนเงินที่ถูกต้อง + + + Please enter a number. + กรุณากรอกตัวเลข + + + The password is invalid. + รหัสผ่านไม่ถูกต้อง + + + Please enter a percentage value. + กรุณากรอกค่าเปอร์เซ็นต์ + + + The values do not match. + ค่าทั้งสองไม่ตรงกัน + + + Please enter a valid time. + กรุณากรอกค่าเวลาที่ถูกต้อง + + + Please select a valid timezone. + กรุณาเลือกค่าเขตเวลาที่ถูกต้อง + + + Please enter a valid URL. + กรุณากรอก URL ที่ถูกต้อง + + + Please enter a valid search term. + กรุณากรอกคำค้นหาที่ถูกต้อง + + + Please provide a valid phone number. + กรุณากรอกเบอร์โทรศัพท์ที่ถูกต้อง + + + The checkbox has an invalid value. + Checkbox มีค่าที่ไม่ถูกต้อง + + + Please enter a valid email address. + กรุณากรอกที่อยู่อีเมล์ที่ถูกต้อง + + + Please select a valid option. + กรุณาเลือกตัวเลือกที่ถูกต้อง + + + Please select a valid range. + กรุณาเลือกค่าช่วงที่ถูกต้อง + + + Please enter a valid week. + กรุณากรอกค่าสัปดาห์ที่ถูกต้อง + + + + diff --git a/src/Symfony/Component/Form/Resources/translations/validators.tl.xlf b/src/Symfony/Component/Form/Resources/translations/validators.tl.xlf index 950c4110d880a..272e331298a2f 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.tl.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.tl.xlf @@ -114,6 +114,26 @@ Please provide a valid phone number. Pakilagay ang balidong numero ng telepono. + + The checkbox has an invalid value. + Ang checkbox ay mayroon hindi balidong halaga. + + + Please enter a valid email address. + Pakilagay ang balidong email address. + + + Please select a valid option. + Pakipiliin ang balidong pagpipilian. + + + Please select a valid range. + Pakipilian ang balidong layo. + + + Please enter a valid week. + Pakilagay ang balidong linggo. + diff --git a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php index cd39b50db440d..4931ccb6a3ed7 100644 --- a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php @@ -2045,7 +2045,7 @@ public function testTextarea() $this->assertWidgetMatchesXpath($form->createView(), [], '/textarea [@name="name"] - [@pattern="foo"] + [not(@pattern)] [.="foo&bar"] ' ); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.php index 3694e49f4292c..e9f9ea6bdc759 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.php @@ -16,7 +16,7 @@ class BooleanToStringTransformerTest extends TestCase { - const TRUE_VALUE = '1'; + private const TRUE_VALUE = '1'; /** * @var BooleanToStringTransformer diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php index 100c54ad462eb..8c71b7bfacefa 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php @@ -18,12 +18,14 @@ use Symfony\Component\Form\Exception\TransformationFailedException; use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper; use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapper; +use Symfony\Component\Form\FileUploadError; use Symfony\Component\Form\Form; use Symfony\Component\Form\FormConfigBuilder; use Symfony\Component\Form\FormError; use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\Tests\Extension\Validator\ViolationMapper\Fixtures\Issue; use Symfony\Component\PropertyAccess\PropertyPath; +use Symfony\Component\Validator\Constraints\File; use Symfony\Component\Validator\ConstraintViolation; use Symfony\Component\Validator\ConstraintViolationInterface; @@ -32,10 +34,10 @@ */ class ViolationMapperTest extends TestCase { - const LEVEL_0 = 0; - const LEVEL_1 = 1; - const LEVEL_1B = 2; - const LEVEL_2 = 3; + private const LEVEL_0 = 0; + private const LEVEL_1 = 1; + private const LEVEL_1B = 2; + private const LEVEL_2 = 3; /** * @var EventDispatcherInterface @@ -81,6 +83,7 @@ protected function getForm($name = 'name', $propertyPath = null, $dataClass = nu $config->setPropertyPath($propertyPath); $config->setCompound(true); $config->setDataMapper(new PropertyPathMapper()); + $config->setErrorBubbling($options['error_bubbling'] ?? false); if (!$synchronized) { $config->addViewTransformer(new CallbackTransformer( @@ -1590,4 +1593,93 @@ public function testBacktrackIfSeveralSubFormsWithSamePropertyPath() $this->assertEquals([$this->getFormError($violation2, $grandChild2)], iterator_to_array($grandChild2->getErrors()), $grandChild2->getName().' should have an error, but has none'); $this->assertEquals([$this->getFormError($violation3, $grandChild3)], iterator_to_array($grandChild3->getErrors()), $grandChild3->getName().' should have an error, but has none'); } + + public function testFileUploadErrorIsNotRemovedIfNoFileSizeConstraintViolationWasRaised() + { + $form = $this->getForm('form'); + $form->addError(new FileUploadError( + 'The file is too large. Allowed maximum size is 2 MB.', + 'The file is too large. Allowed maximum size is {{ limit }} {{ suffix }}.', + [ + '{{ limit }}' => '2', + '{{ suffix }}' => 'MB', + ] + )); + + $this->mapper->mapViolation($this->getConstraintViolation('data'), $form); + + $this->assertCount(2, $form->getErrors()); + } + + public function testFileUploadErrorIsRemovedIfFileSizeConstraintViolationWasRaised() + { + $form = $this->getForm('form'); + $form->addError(new FileUploadError( + 'The file is too large. Allowed maximum size is 2 MB.', + 'The file is too large. Allowed maximum size is {{ limit }} {{ suffix }}.', + [ + '{{ limit }}' => '2', + '{{ suffix }}' => 'MB', + ] + )); + + $violation = new ConstraintViolation( + 'The file is too large (3 MB). Allowed maximum size is 2 MB.', + 'The file is too large ({{ size }} {{ suffix }}). Allowed maximum size is {{ limit }} {{ suffix }}.', + [ + '{{ limit }}' => '2', + '{{ size }}' => '3', + '{{ suffix }}' => 'MB', + ], + '', + 'data', + null, + null, + (string) \UPLOAD_ERR_INI_SIZE, + new File() + ); + $this->mapper->mapViolation($this->getConstraintViolation('data'), $form); + $this->mapper->mapViolation($violation, $form); + + $this->assertCount(2, $form->getErrors()); + } + + public function testFileUploadErrorIsRemovedIfFileSizeConstraintViolationWasRaisedOnFieldWithErrorBubbling() + { + $parent = $this->getForm('parent'); + $child = $this->getForm('child', 'file', null, [], false, true, [ + 'error_bubbling' => true, + ]); + $parent->add($child); + $child->addError(new FileUploadError( + 'The file is too large. Allowed maximum size is 2 MB.', + 'The file is too large. Allowed maximum size is {{ limit }} {{ suffix }}.', + [ + '{{ limit }}' => '2', + '{{ suffix }}' => 'MB', + ] + )); + + $violation = new ConstraintViolation( + 'The file is too large (3 MB). Allowed maximum size is 2 MB.', + 'The file is too large ({{ size }} {{ suffix }}). Allowed maximum size is {{ limit }} {{ suffix }}.', + [ + '{{ limit }}' => '2', + '{{ size }}' => '3', + '{{ suffix }}' => 'MB', + ], + null, + 'data.file', + null, + null, + (string) \UPLOAD_ERR_INI_SIZE, + new File() + ); + $this->mapper->mapViolation($this->getConstraintViolation('data'), $parent); + $this->mapper->mapViolation($this->getConstraintViolation('data.file'), $parent); + $this->mapper->mapViolation($violation, $parent); + + $this->assertCount(3, $parent->getErrors()); + $this->assertCount(0, $child->getErrors()); + } } diff --git a/src/Symfony/Component/Form/Tests/Resources/TranslationFilesTest.php b/src/Symfony/Component/Form/Tests/Resources/TranslationFilesTest.php index 3b0d926c2ee7a..5a9669e92b424 100644 --- a/src/Symfony/Component/Form/Tests/Resources/TranslationFilesTest.php +++ b/src/Symfony/Component/Form/Tests/Resources/TranslationFilesTest.php @@ -12,7 +12,7 @@ namespace Symfony\Component\Form\Tests\Resources; use PHPUnit\Framework\TestCase; -use PHPUnit\Util\Xml\Loader; +use Symfony\Component\Translation\Util\XliffUtils; class TranslationFilesTest extends TestCase { @@ -21,13 +21,27 @@ class TranslationFilesTest extends TestCase */ public function testTranslationFileIsValid($filePath) { - $loader = class_exists(Loader::class) - ? [new Loader(), 'loadFile'] - : ['PHPUnit\Util\XML', 'loadfile']; + $document = new \DOMDocument(); + $document->loadXML(file_get_contents($filePath)); - $loader($filePath, false, false, true); + $errors = XliffUtils::validateSchema($document); - $this->addToAssertionCount(1); + $this->assertCount(0, $errors, sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message')))); + } + + /** + * @dataProvider provideTranslationFiles + * @group Legacy + */ + public function testTranslationFileIsValidWithoutEntityLoader($filePath) + { + $document = new \DOMDocument(); + $document->loadXML(file_get_contents($filePath)); + libxml_disable_entity_loader(true); + + $errors = XliffUtils::validateSchema($document); + + $this->assertCount(0, $errors, sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message')))); } public function provideTranslationFiles() diff --git a/src/Symfony/Component/Form/composer.json b/src/Symfony/Component/Form/composer.json index b17ef9804901d..8d9d8f9623588 100644 --- a/src/Symfony/Component/Form/composer.json +++ b/src/Symfony/Component/Form/composer.json @@ -29,7 +29,7 @@ }, "require-dev": { "doctrine/collections": "~1.0", - "symfony/validator": "^4.4.12|^5.1.6", + "symfony/validator": "^4.4.17|^5.1.9", "symfony/dependency-injection": "^4.4|^5.0", "symfony/expression-language": "^4.4|^5.0", "symfony/config": "^4.4|^5.0", diff --git a/src/Symfony/Component/HttpClient/AmpHttpClient.php b/src/Symfony/Component/HttpClient/AmpHttpClient.php index 0877b8883bef7..b53d636b9cf2f 100644 --- a/src/Symfony/Component/HttpClient/AmpHttpClient.php +++ b/src/Symfony/Component/HttpClient/AmpHttpClient.php @@ -82,6 +82,15 @@ public function request(string $method, string $url, array $options = []): Respo throw new \LogicException('You cannot use the "proxy" option as the "amphp/http-tunnel" package is not installed. Try running "composer require amphp/http-tunnel".'); } + if ($options['bindto']) { + if (0 === strpos($options['bindto'], 'if!')) { + throw new TransportException(__CLASS__.' cannot bind to network interfaces, use e.g. CurlHttpClient instead.'); + } + if (0 === strpos($options['bindto'], 'host!')) { + $options['bindto'] = substr($options['bindto'], 5); + } + } + if ('' !== $options['body'] && 'POST' === $method && !isset($options['normalized_headers']['content-type'])) { $options['headers'][] = 'Content-Type: application/x-www-form-urlencoded'; } @@ -141,7 +150,7 @@ public function stream($responses, float $timeout = null): ResponseStreamInterfa if ($responses instanceof AmpResponse) { $responses = [$responses]; } elseif (!is_iterable($responses)) { - throw new \TypeError(sprintf('%s() expects parameter 1 to be an iterable of AmpResponse objects, %s given.', __METHOD__, get_debug_type($responses))); + throw new \TypeError(sprintf('"%s()" expects parameter 1 to be an iterable of AmpResponse objects, "%s" given.', __METHOD__, get_debug_type($responses))); } return new ResponseStream(AmpResponse::stream($responses, $timeout)); diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php index ce442834a166f..2a46fc92cf6fe 100644 --- a/src/Symfony/Component/HttpClient/CurlHttpClient.php +++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php @@ -269,7 +269,7 @@ public function request(string $method, string $url, array $options = []): Respo if ($options['bindto']) { if (file_exists($options['bindto'])) { $curlopts[\CURLOPT_UNIX_SOCKET_PATH] = $options['bindto']; - } elseif (preg_match('/^(.*):(\d+)$/', $options['bindto'], $matches)) { + } elseif (0 !== strpos($options['bindto'], 'if!') && preg_match('/^(.*):(\d+)$/', $options['bindto'], $matches)) { $curlopts[\CURLOPT_INTERFACE] = $matches[1]; $curlopts[\CURLOPT_LOCALPORT] = $matches[2]; } else { diff --git a/src/Symfony/Component/HttpClient/NativeHttpClient.php b/src/Symfony/Component/HttpClient/NativeHttpClient.php index 93e7c36cfe8ce..01e5440e2bb3d 100644 --- a/src/Symfony/Component/HttpClient/NativeHttpClient.php +++ b/src/Symfony/Component/HttpClient/NativeHttpClient.php @@ -67,8 +67,16 @@ public function request(string $method, string $url, array $options = []): Respo { [$url, $options] = self::prepareRequest($method, $url, $options, $this->defaultOptions); - if ($options['bindto'] && file_exists($options['bindto'])) { - throw new TransportException(__CLASS__.' cannot bind to local Unix sockets, use e.g. CurlHttpClient instead.'); + if ($options['bindto']) { + if (file_exists($options['bindto'])) { + throw new TransportException(__CLASS__.' cannot bind to local Unix sockets, use e.g. CurlHttpClient instead.'); + } + if (0 === strpos($options['bindto'], 'if!')) { + throw new TransportException(__CLASS__.' cannot bind to network interfaces, use e.g. CurlHttpClient instead.'); + } + if (0 === strpos($options['bindto'], 'host!')) { + $options['bindto'] = substr($options['bindto'], 5); + } } $options['body'] = self::getBodyAsString($options['body']); diff --git a/src/Symfony/Component/HttpClient/Response/CurlResponse.php b/src/Symfony/Component/HttpClient/Response/CurlResponse.php index 435f6402c796d..9709a189f5926 100644 --- a/src/Symfony/Component/HttpClient/Response/CurlResponse.php +++ b/src/Symfony/Component/HttpClient/Response/CurlResponse.php @@ -283,10 +283,7 @@ private static function perform(ClientState $multi, array &$responses = null): v curl_multi_remove_handle($multi->handle, $ch); $waitFor[1] = (string) ((int) $waitFor[1] - 1); // decrement the retry counter curl_setopt($ch, \CURLOPT_PRIVATE, $waitFor); - - if ('1' === $waitFor[1]) { - curl_setopt($ch, \CURLOPT_HTTP_VERSION, \CURL_HTTP_VERSION_1_1); - } + curl_setopt($ch, \CURLOPT_FORBID_REUSE, true); if (0 === curl_multi_add_handle($multi->handle, $ch)) { continue; diff --git a/src/Symfony/Component/HttpClient/Response/TraceableResponse.php b/src/Symfony/Component/HttpClient/Response/TraceableResponse.php index 2fe78f45748bc..b574310253de5 100644 --- a/src/Symfony/Component/HttpClient/Response/TraceableResponse.php +++ b/src/Symfony/Component/HttpClient/Response/TraceableResponse.php @@ -52,24 +52,24 @@ public function getHeaders(bool $throw = true): array public function getContent(bool $throw = true): string { - $this->content = $this->response->getContent(false); - - if ($throw) { - $this->checkStatusCode($this->response->getStatusCode()); + try { + return $this->content = $this->response->getContent(false); + } finally { + if ($throw) { + $this->checkStatusCode($this->response->getStatusCode()); + } } - - return $this->content; } public function toArray(bool $throw = true): array { - $this->content = $this->response->toArray(false); - - if ($throw) { - $this->checkStatusCode($this->response->getStatusCode()); + try { + return $this->content = $this->response->toArray(false); + } finally { + if ($throw) { + $this->checkStatusCode($this->response->getStatusCode()); + } } - - return $this->content; } public function cancel(): void diff --git a/src/Symfony/Component/HttpClient/Tests/TraceableHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/TraceableHttpClientTest.php index 9b6752f7b62cd..43012e99c20d5 100755 --- a/src/Symfony/Component/HttpClient/Tests/TraceableHttpClientTest.php +++ b/src/Symfony/Component/HttpClient/Tests/TraceableHttpClientTest.php @@ -16,6 +16,7 @@ use Symfony\Component\HttpClient\NativeHttpClient; use Symfony\Component\HttpClient\Response\MockResponse; use Symfony\Component\HttpClient\TraceableHttpClient; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\Test\TestHttpServer; @@ -100,4 +101,16 @@ public function testStream() $this->assertGreaterThan(1, \count($chunks)); $this->assertSame('Symfony is awesome!', implode('', $chunks)); } + + public function testToArrayChecksStatusCodeBeforeDecoding() + { + $this->expectException(ClientExceptionInterface::class); + + $sut = new TraceableHttpClient(new MockHttpClient($responseFactory = function (): MockResponse { + return new MockResponse('Errored.', ['http_code' => 400]); + })); + + $response = $sut->request('GET', 'https://example.com/foo/bar'); + $response->toArray(); + } } diff --git a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php index 74bad652bb61f..518aa1d343806 100644 --- a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php +++ b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php @@ -217,7 +217,7 @@ public function prepare(Request $request) // @link https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/#x-accel-redirect $parts = HeaderUtils::split($request->headers->get('X-Accel-Mapping', ''), ',='); foreach ($parts as $part) { - list($pathPrefix, $location) = $part; + [$pathPrefix, $location] = $part; if (substr($path, 0, \strlen($pathPrefix)) === $pathPrefix) { $path = $location.substr($path, \strlen($pathPrefix)); // Only set X-Accel-Redirect header if a valid URI can be produced @@ -237,7 +237,7 @@ public function prepare(Request $request) $range = $request->headers->get('Range'); if (0 === strpos($range, 'bytes=')) { - list($start, $end) = explode('-', substr($range, 6), 2) + [0]; + [$start, $end] = explode('-', substr($range, 6), 2) + [0]; $end = ('' === $end) ? $fileSize - 1 : (int) $end; diff --git a/src/Symfony/Component/HttpFoundation/IpUtils.php b/src/Symfony/Component/HttpFoundation/IpUtils.php index 0599ce553ef5f..27fe725929f7d 100644 --- a/src/Symfony/Component/HttpFoundation/IpUtils.php +++ b/src/Symfony/Component/HttpFoundation/IpUtils.php @@ -71,7 +71,7 @@ public static function checkIp4(?string $requestIp, string $ip) } if (false !== strpos($ip, '/')) { - list($address, $netmask) = explode('/', $ip, 2); + [$address, $netmask] = explode('/', $ip, 2); if ('0' === $netmask) { return self::$checkedIps[$cacheKey] = filter_var($address, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4); @@ -118,7 +118,7 @@ public static function checkIp6(?string $requestIp, string $ip) } if (false !== strpos($ip, '/')) { - list($address, $netmask) = explode('/', $ip, 2); + [$address, $netmask] = explode('/', $ip, 2); if ('0' === $netmask) { return (bool) unpack('n*', @inet_pton($address)); diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 60fe3bf55220a..eeb37045758ef 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -180,7 +180,7 @@ class Request protected $format; /** - * @var SessionInterface + * @var SessionInterface|callable */ protected $session; @@ -1850,9 +1850,15 @@ protected function prepareBaseUrl() } $basename = basename($baseUrl); - if (empty($basename) || !strpos(rawurldecode($truncatedRequestUri), $basename)) { - // no match whatsoever; set it blank - return ''; + if (empty($basename) || !strpos(rawurldecode($truncatedRequestUri).'/', '/'.$basename.'/')) { + // strip autoindex filename, for virtualhost based on URL path + $baseUrl = \dirname($baseUrl).'/'; + + $basename = basename($baseUrl); + if (empty($basename) || !strpos(rawurldecode($truncatedRequestUri).'/', '/'.$basename.'/')) { + // no match whatsoever; set it blank + return ''; + } } // If using mod_rewrite or ISAPI_Rewrite strip the script filename diff --git a/src/Symfony/Component/HttpFoundation/ServerBag.php b/src/Symfony/Component/HttpFoundation/ServerBag.php index 02c70911c19f1..ed2d7812ac31b 100644 --- a/src/Symfony/Component/HttpFoundation/ServerBag.php +++ b/src/Symfony/Component/HttpFoundation/ServerBag.php @@ -66,7 +66,7 @@ public function getHeaders() // Decode AUTHORIZATION header into PHP_AUTH_USER and PHP_AUTH_PW when authorization header is basic $exploded = explode(':', base64_decode(substr($authorizationHeader, 6)), 2); if (2 == \count($exploded)) { - list($headers['PHP_AUTH_USER'], $headers['PHP_AUTH_PW']) = $exploded; + [$headers['PHP_AUTH_USER'], $headers['PHP_AUTH_PW']] = $exploded; } } elseif (empty($this->parameters['PHP_AUTH_DIGEST']) && (0 === stripos($authorizationHeader, 'digest '))) { // In some circumstances PHP_AUTH_DIGEST needs to be set diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index 99cd54884f51c..b1490dea6e5ba 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -1757,6 +1757,36 @@ public function getBaseUrlData() '/foo', '/bar+baz', ], + [ + '/sub/foo/bar', + [ + 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo/app.php', + 'SCRIPT_NAME' => '/foo/app.php', + 'PHP_SELF' => '/foo/app.php', + ], + '/sub/foo', + '/bar', + ], + [ + '/sub/foo/app.php/bar', + [ + 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo/app.php', + 'SCRIPT_NAME' => '/foo/app.php', + 'PHP_SELF' => '/foo/app.php', + ], + '/sub/foo/app.php', + '/bar', + ], + [ + '/sub/foo/bar/baz', + [ + 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo/app2.phpx', + 'SCRIPT_NAME' => '/foo/app2.phpx', + 'PHP_SELF' => '/foo/app2.phpx', + ], + '/sub/foo', + '/bar/baz', + ], ]; } diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php index e9c17703a7173..6fe43a0027aef 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php @@ -20,8 +20,8 @@ */ class MemcachedSessionHandlerTest extends TestCase { - const PREFIX = 'prefix_'; - const TTL = 1000; + private const PREFIX = 'prefix_'; + private const TTL = 1000; /** * @var MemcachedSessionHandler diff --git a/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php b/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php index cffe45904dcf5..300ee020c2854 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php @@ -114,7 +114,7 @@ protected function createController(string $controller) return $controller; } - list($class, $method) = explode('::', $controller, 2); + [$class, $method] = explode('::', $controller, 2); try { $controller = [$this->instantiateController($class), $method]; @@ -172,7 +172,7 @@ private function getControllerError($callable): string return 'Invalid array callable, expected [controller, method].'; } - list($controller, $method) = $callable; + [$controller, $method] = $callable; if (\is_string($controller) && !class_exists($controller)) { return sprintf('Class "%s" does not exist.', $controller); diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php index ba45a2ddce783..33150ed0e1eae 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php @@ -75,7 +75,7 @@ public function dump(Data $data) $this->stopwatch->start('dump'); } - list('name' => $name, 'file' => $file, 'line' => $line, 'file_excerpt' => $fileExcerpt) = $this->sourceContextProvider->getContext(); + ['name' => $name, 'file' => $file, 'line' => $line, 'file_excerpt' => $fileExcerpt] = $this->sourceContextProvider->getContext(); if ($this->dumper instanceof Connection) { if (!$this->dumper->write($data)) { diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php index 286f212f9a182..a2a9a0c71b1c9 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php @@ -106,7 +106,7 @@ public function process(ContainerBuilder $container) if (!isset($methods[$action = strtolower($attributes['action'])])) { throw new InvalidArgumentException(sprintf('Invalid "action" attribute on tag "%s" for service "%s": no public "%s()" method found on class "%s".', $this->controllerTag, $id, $attributes['action'], $class)); } - list($r, $parameters) = $methods[$action]; + [$r, $parameters] = $methods[$action]; $found = false; foreach ($parameters as $p) { @@ -124,7 +124,7 @@ public function process(ContainerBuilder $container) } } - foreach ($methods as list($r, $parameters)) { + foreach ($methods as [$r, $parameters]) { /** @var \ReflectionMethod $r */ // create a per-method map of argument-names to service/type-references @@ -146,7 +146,7 @@ public function process(ContainerBuilder $container) } elseif (isset($bindings[$bindingName = $type.' $'.$p->name]) || isset($bindings[$bindingName = '$'.$p->name]) || isset($bindings[$bindingName = $type])) { $binding = $bindings[$bindingName]; - list($bindingValue, $bindingId, , $bindingType, $bindingFile) = $binding->getValues(); + [$bindingValue, $bindingId, , $bindingType, $bindingFile] = $binding->getValues(); $binding->setValues([$bindingValue, $bindingId, true, $bindingType, $bindingFile]); if (!$bindingValue instanceof Reference) { diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php index f69d37619e446..5f54e9c164d5f 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php @@ -42,14 +42,14 @@ public function process(ContainerBuilder $container) } else { // any methods listed for call-at-instantiation cannot be actions $reason = false; - list($id, $action) = explode('::', $controller); + [$id, $action] = explode('::', $controller); if ($container->hasAlias($id)) { continue; } $controllerDef = $container->getDefinition($id); - foreach ($controllerDef->getMethodCalls() as list($method)) { + foreach ($controllerDef->getMethodCalls() as [$method]) { if (0 === strcasecmp($action, $method)) { $reason = sprintf('Removing method "%s" of service "%s" from controller candidates: the method is called at instantiation, thus cannot be an action.', $action, $id); break; diff --git a/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php b/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php index 1fe3264f7d305..f0bbdaaa85dd1 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php @@ -58,12 +58,9 @@ public function onKernelRequest(RequestEvent $event) $session = null; $request = $event->getRequest(); - if ($request->hasSession()) { - // no-op - } elseif (method_exists($request, 'setSessionFactory')) { - $request->setSessionFactory(function () { return $this->getSession(); }); - } elseif ($session = $this->getSession()) { - $request->setSession($session); + if (!$request->hasSession()) { + $sess = null; + $request->setSessionFactory(function () use (&$sess) { return $sess ?? $sess = $this->getSession(); }); } $session = $session ?? ($this->container && $this->container->has('initialized_session') ? $this->container->get('initialized_session') : null); diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 59689807370d0..4134ff870e331 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -73,11 +73,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private static $freshCache = []; - const VERSION = '5.1.8'; - const VERSION_ID = 50108; + const VERSION = '5.1.9'; + const VERSION_ID = 50109; const MAJOR_VERSION = 5; const MINOR_VERSION = 1; - const RELEASE_VERSION = 8; + const RELEASE_VERSION = 9; const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '01/2021'; @@ -245,7 +245,7 @@ public function locateResource(string $name) $bundleName = substr($name, 1); $path = ''; if (false !== strpos($bundleName, '/')) { - list($bundleName, $path) = explode('/', $bundleName, 2); + [$bundleName, $path] = explode('/', $bundleName, 2); } $bundle = $this->getBundle($bundleName); diff --git a/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php b/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php index 4f63b41889c25..1d95ead92a978 100644 --- a/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php +++ b/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php @@ -61,7 +61,7 @@ public function find(?string $ip, ?string $url, ?int $limit, ?string $method, in $result = []; while (\count($result) < $limit && $line = $this->readLineFromFile($file)) { $values = str_getcsv($line); - list($csvToken, $csvIp, $csvMethod, $csvUrl, $csvTime, $csvParent, $csvStatusCode) = $values; + [$csvToken, $csvIp, $csvMethod, $csvUrl, $csvTime, $csvParent, $csvStatusCode] = $values; $csvTime = (int) $csvTime; if ($ip && false === strpos($csvIp, $ip) || $url && false === strpos($csvUrl, $url) || $method && false === strpos($csvMethod, $method) || $statusCode && false === strpos($csvStatusCode, $statusCode)) { diff --git a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ControllerArgumentValueResolverPassTest.php b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ControllerArgumentValueResolverPassTest.php index 2694d002cf5b6..c95a7fb52468c 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ControllerArgumentValueResolverPassTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ControllerArgumentValueResolverPassTest.php @@ -39,7 +39,7 @@ public function testServicesAreOrderedAccordingToPriority() $container = new ContainerBuilder(); $container->setDefinition('argument_resolver', $definition); - foreach ($services as $id => list($tag)) { + foreach ($services as $id => [$tag]) { $container->register($id)->addTag('controller.argument_value_resolver', $tag); } @@ -72,7 +72,7 @@ public function testInDebugWithStopWatchDefinition() $container->register('debug.stopwatch', Stopwatch::class); $container->setDefinition('argument_resolver', $definition); - foreach ($services as $id => list($tag)) { + foreach ($services as $id => [$tag]) { $container->register($id)->addTag('controller.argument_value_resolver', $tag); } diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php index 8df2ce51698e9..4588d412ab767 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php @@ -27,6 +27,7 @@ use Symfony\Component\HttpKernel\EventListener\SessionListener; use Symfony\Component\HttpKernel\Exception\UnexpectedSessionUsageException; use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\KernelInterface; class SessionListenerTest extends TestCase { @@ -181,6 +182,38 @@ public function testSurrogateMasterRequestIsPublic() $this->assertLessThanOrEqual((new \DateTime('now', new \DateTimeZone('UTC'))), (new \DateTime($response->headers->get('Expires')))); } + public function testGetSessionIsCalledOnce() + { + $session = $this->getMockBuilder(Session::class)->disableOriginalConstructor()->getMock(); + $sessionStorage = $this->getMockBuilder(NativeSessionStorage::class)->getMock(); + $kernel = $this->getMockBuilder(KernelInterface::class)->getMock(); + + $sessionStorage->expects($this->once()) + ->method('setOptions') + ->with(['cookie_secure' => true]); + + $requestStack = new RequestStack(); + $requestStack->push($masterRequest = new Request([], [], [], [], [], ['HTTPS' => 'on'])); + + $container = new Container(); + $container->set('session_storage', $sessionStorage); + $container->set('session', $session); + $container->set('request_stack', $requestStack); + + $event = new RequestEvent($kernel, $masterRequest, HttpKernelInterface::MASTER_REQUEST); + + $listener = new SessionListener($container); + $listener->onKernelRequest($event); + + $subRequest = $masterRequest->duplicate(); + // at this point both master and subrequest have a closure to build the session + + $masterRequest->getSession(); + + // calling the factory on the subRequest should not trigger a second call to storage->sesOptions() + $subRequest->getSession(); + } + public function testSessionUsageExceptionIfStatelessAndSessionUsed() { $session = $this->getMockBuilder(Session::class)->disableOriginalConstructor()->getMock(); diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php index fd67af368b740..62179ba154fcd 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php @@ -239,6 +239,7 @@ public function testResponseIsExpirableButNotValidateableWhenMasterResponseCombi } /** + * @group time-sensitive * @dataProvider cacheControlMergingProvider */ public function testCacheControlMerging(array $expects, array $master, array $surrogates) diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php index 1f5f472802e7a..fc1ef64663cf7 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php @@ -92,7 +92,7 @@ public function testSetsTheXContentDigestResponseHeaderBeforeStoring() { $cacheKey = $this->storeSimpleEntry(); $entries = $this->getStoreMetadata($cacheKey); - list(, $res) = $entries[0]; + [, $res] = $entries[0]; $this->assertEquals('en9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', $res['x-content-digest'][0]); } @@ -103,7 +103,7 @@ public function testDoesNotTrustXContentDigestFromUpstream() $cacheKey = $this->store->write($this->request, $response); $entries = $this->getStoreMetadata($cacheKey); - list(, $res) = $entries[0]; + [, $res] = $entries[0]; $this->assertEquals('en9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', $res['x-content-digest'][0]); $this->assertEquals('en9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', $response->headers->get('X-Content-Digest')); diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/TestHttpKernel.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/TestHttpKernel.php index 933ed9b27c8c6..7f05fb8b89307 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/TestHttpKernel.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/TestHttpKernel.php @@ -43,13 +43,13 @@ public function assert(\Closure $callback) { $trustedConfig = [Request::getTrustedProxies(), Request::getTrustedHeaderSet()]; - list($trustedProxies, $trustedHeaderSet, $backendRequest) = $this->backendRequest; + [$trustedProxies, $trustedHeaderSet, $backendRequest] = $this->backendRequest; Request::setTrustedProxies($trustedProxies, $trustedHeaderSet); try { $callback($backendRequest); } finally { - list($trustedProxies, $trustedHeaderSet) = $trustedConfig; + [$trustedProxies, $trustedHeaderSet] = $trustedConfig; Request::setTrustedProxies($trustedProxies, $trustedHeaderSet); } } diff --git a/src/Symfony/Component/Intl/Tests/Data/Bundle/Reader/BundleEntryReaderTest.php b/src/Symfony/Component/Intl/Tests/Data/Bundle/Reader/BundleEntryReaderTest.php index 482999c60dd5f..3624295353161 100644 --- a/src/Symfony/Component/Intl/Tests/Data/Bundle/Reader/BundleEntryReaderTest.php +++ b/src/Symfony/Component/Intl/Tests/Data/Bundle/Reader/BundleEntryReaderTest.php @@ -21,7 +21,7 @@ */ class BundleEntryReaderTest extends TestCase { - const RES_DIR = '/res/dir'; + private const RES_DIR = '/res/dir'; /** * @var BundleEntryReader diff --git a/src/Symfony/Component/Intl/Tests/DateFormatter/Verification/IntlDateFormatterTest.php b/src/Symfony/Component/Intl/Tests/DateFormatter/Verification/IntlDateFormatterTest.php index 50cccd993b8fa..ed82ead532fd3 100644 --- a/src/Symfony/Component/Intl/Tests/DateFormatter/Verification/IntlDateFormatterTest.php +++ b/src/Symfony/Component/Intl/Tests/DateFormatter/Verification/IntlDateFormatterTest.php @@ -30,6 +30,18 @@ protected function setUp(): void parent::setUp(); } + /** + * @dataProvider formatProvider + */ + public function testFormat($pattern, $timestamp, $expected) + { + if (\PHP_VERSION_ID < 70105 && $timestamp instanceof \DateTimeImmutable) { + $this->markTestSkipped('PHP >= 7.1.5 required for DateTimeImmutable.'); + } + + parent::testFormat($pattern, $timestamp, $expected); + } + /** * @dataProvider formatTimezoneProvider */ diff --git a/src/Symfony/Component/Intl/Tests/Util/GitRepositoryTest.php b/src/Symfony/Component/Intl/Tests/Util/GitRepositoryTest.php index ecbeb0eb520a5..5868174332def 100644 --- a/src/Symfony/Component/Intl/Tests/Util/GitRepositoryTest.php +++ b/src/Symfony/Component/Intl/Tests/Util/GitRepositoryTest.php @@ -23,7 +23,7 @@ class GitRepositoryTest extends TestCase { private $targetDir; - const REPO_URL = 'https://github.com/symfony/intl.git'; + private const REPO_URL = 'https://github.com/symfony/intl.git'; /** * @before diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php index 33d3bb957f808..7aa9789e98b95 100644 --- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php +++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php @@ -151,8 +151,9 @@ public function applyOperations(string $dn, iterable $operations): void $operationsMapped[] = $modification->toArray(); } - if (!@ldap_modify_batch($this->getConnectionResource(), $dn, $operationsMapped)) { - throw new UpdateOperationException(sprintf('Error executing UpdateOperation on "%s": ', $dn).ldap_error($this->getConnectionResource()), ldap_errno($con)); + $con = $this->getConnectionResource(); + if (!@ldap_modify_batch($con, $dn, $operationsMapped)) { + throw new UpdateOperationException(sprintf('Error executing UpdateOperation on "%s": ', $dn).ldap_error($con), ldap_errno($con)); } } diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php index 0ea9aa3b86dd4..497c8313156ba 100644 --- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php +++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php @@ -100,7 +100,7 @@ public function execute() $cookie = ''; do { if ($pageControl) { - $this->controlPagedResult($con, $pageSize, $cookie); + $this->controlPagedResult($con, $pageSize, true, $cookie); } $sizeLimit = $itemsLeft; if ($pageSize > 0 && $sizeLimit >= $pageSize) { @@ -174,7 +174,7 @@ public function getResources(): array private function resetPagination() { $con = $this->connection->getResource(); - $this->controlPagedResultResponse($con, 0, ''); + $this->controlPagedResult($con, 0, false, ''); $this->serverctrls = []; // This is a workaround for a bit of a bug in the above invocation @@ -204,15 +204,15 @@ private function resetPagination() * * @param resource $con */ - private function controlPagedResult($con, int $pageSize, string $cookie): bool + private function controlPagedResult($con, int $pageSize, bool $critical, string $cookie): bool { if (\PHP_VERSION_ID < 70300) { - return ldap_control_paged_result($con, $pageSize, true, $cookie); + return ldap_control_paged_result($con, $pageSize, $critical, $cookie); } $this->serverctrls = [ [ 'oid' => \LDAP_CONTROL_PAGEDRESULTS, - 'isCritical' => true, + 'isCritical' => $critical, 'value' => [ 'size' => $pageSize, 'cookie' => $cookie, diff --git a/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/AdapterTest.php b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/AdapterTest.php index d17b47b1fadd9..566ba215dc6d8 100644 --- a/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/AdapterTest.php +++ b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/AdapterTest.php @@ -22,15 +22,10 @@ /** * @requires extension ldap + * @group integration */ class AdapterTest extends LdapTestCase { - const PAGINATION_REQUIRED_CONFIG = [ - 'options' => [ - 'protocol_version' => 3, - ], - ]; - public function testLdapEscape() { $ldap = new Adapter(); @@ -122,7 +117,7 @@ public function testLdapQueryScopeOneLevel() public function testLdapPagination() { - $ldap = new Adapter(array_merge($this->getLdapConfig(), static::PAGINATION_REQUIRED_CONFIG)); + $ldap = new Adapter($this->getLdapConfig()); $ldap->getConnection()->bind('cn=admin,dc=symfony,dc=com', 'symfony'); $entries = $this->setupTestUsers($ldap); @@ -153,7 +148,7 @@ public function testLdapPagination() $this->assertEquals(\count($fully_paged_query->getResources()), 1); $this->assertEquals(\count($paged_query->getResources()), 5); - if (\PHP_MAJOR_VERSION > 7 || (\PHP_MAJOR_VERSION == 7 && \PHP_MINOR_VERSION >= 2)) { + if (\PHP_VERSION_ID >= 70200) { // This last query is to ensure that we haven't botched the state of our connection // by not resetting pagination properly. extldap <= PHP 7.1 do not implement the necessary // bits to work around an implementation flaw, so we simply can't guarantee this to work there. @@ -205,7 +200,7 @@ private function destroyEntries($ldap, $entries) public function testLdapPaginationLimits() { - $ldap = new Adapter(array_merge($this->getLdapConfig(), static::PAGINATION_REQUIRED_CONFIG)); + $ldap = new Adapter($this->getLdapConfig()); $ldap->getConnection()->bind('cn=admin,dc=symfony,dc=com', 'symfony'); $entries = $this->setupTestUsers($ldap); diff --git a/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/LdapManagerTest.php b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/LdapManagerTest.php index e23e8018547bf..bfdd7d5342ff5 100644 --- a/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/LdapManagerTest.php +++ b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/LdapManagerTest.php @@ -15,7 +15,6 @@ use Symfony\Component\Ldap\Adapter\ExtLdap\Collection; use Symfony\Component\Ldap\Adapter\ExtLdap\UpdateOperation; use Symfony\Component\Ldap\Entry; -use Symfony\Component\Ldap\Exception\AlreadyExistsException; use Symfony\Component\Ldap\Exception\LdapException; use Symfony\Component\Ldap\Exception\NotBoundException; use Symfony\Component\Ldap\Exception\UpdateOperationException; @@ -23,6 +22,7 @@ /** * @requires extension ldap + * @group integration */ class LdapManagerTest extends LdapTestCase { @@ -82,7 +82,7 @@ public function testLdapAddInvalidEntry() */ public function testLdapAddDouble() { - $this->expectException(AlreadyExistsException::class); + $this->expectException(LdapException::class); $this->executeSearchQuery(1); $entry = new Entry('cn=Elsa Amrouche,dc=symfony,dc=com', [ @@ -94,7 +94,11 @@ public function testLdapAddDouble() $em = $this->adapter->getEntryManager(); $em->add($entry); - $em->add($entry); + try { + $em->add($entry); + } finally { + $em->remove($entry); + } } /** @@ -210,11 +214,12 @@ public function testLdapRenameWithoutRemovingOldRdn() $newEntry = $result[0]; $originalCN = $entry->getAttribute('cn')[0]; - $this->assertStringContainsString($originalCN, $newEntry->getAttribute('cn')); - - $entryManager->rename($newEntry, 'cn='.$originalCN); - - $this->executeSearchQuery(1); + try { + $this->assertContains($originalCN, $newEntry->getAttribute('cn')); + $this->assertContains('Kevin', $newEntry->getAttribute('cn')); + } finally { + $entryManager->rename($newEntry, 'cn='.$originalCN); + } } public function testLdapAddRemoveAttributeValues() @@ -372,7 +377,7 @@ public function testLdapMove() $result = $this->executeSearchQuery(1); $entry = $result[0]; - $this->assertNotContains('ou=Ldap', $entry->getDn()); + $this->assertStringNotContainsString('ou=Ldap', $entry->getDn()); $entryManager = $this->adapter->getEntryManager(); $entryManager->move($entry, 'ou=Ldap,ou=Components,dc=symfony,dc=com'); @@ -380,5 +385,8 @@ public function testLdapMove() $result = $this->executeSearchQuery(1); $movedEntry = $result[0]; $this->assertStringContainsString('ou=Ldap', $movedEntry->getDn()); + + // Move back entry + $entryManager->move($movedEntry, 'dc=symfony,dc=com'); } } diff --git a/src/Symfony/Component/Ldap/Tests/Fixtures/conf/slapd.conf b/src/Symfony/Component/Ldap/Tests/Fixtures/conf/slapd.conf deleted file mode 100644 index 24eebcb2dbe33..0000000000000 --- a/src/Symfony/Component/Ldap/Tests/Fixtures/conf/slapd.conf +++ /dev/null @@ -1,18 +0,0 @@ -# See slapd.conf(5) for details on configuration options. -include /etc/ldap/schema/core.schema -include /etc/ldap/schema/cosine.schema -include /etc/ldap/schema/inetorgperson.schema -include /etc/ldap/schema/nis.schema - -pidfile /tmp/slapd/slapd.pid -argsfile /tmp/slapd/slapd.args - -modulepath /tmp/slapd-modules -moduleload back_hdb - -database hdb -directory /tmp/slapd - -suffix "dc=symfony,dc=com" -rootdn "cn=admin,dc=symfony,dc=com" -rootpw {SSHA}btWUi971ytYpVMbZLkaQ2A6ETh3VA0lL diff --git a/src/Symfony/Component/Ldap/Tests/Fixtures/data/base.ldif b/src/Symfony/Component/Ldap/Tests/Fixtures/data/base.ldif deleted file mode 100644 index 25abb296c9a6c..0000000000000 --- a/src/Symfony/Component/Ldap/Tests/Fixtures/data/base.ldif +++ /dev/null @@ -1,4 +0,0 @@ -dn: dc=symfony,dc=com -objectClass: dcObject -objectClass: organizationalUnit -ou: Organization diff --git a/src/Symfony/Component/Ldap/Tests/LdapTestCase.php b/src/Symfony/Component/Ldap/Tests/LdapTestCase.php index 9a1424a62e8f7..606065e491e36 100644 --- a/src/Symfony/Component/Ldap/Tests/LdapTestCase.php +++ b/src/Symfony/Component/Ldap/Tests/LdapTestCase.php @@ -9,6 +9,7 @@ class LdapTestCase extends TestCase protected function getLdapConfig() { $h = @ldap_connect(getenv('LDAP_HOST'), getenv('LDAP_PORT')); + @ldap_set_option($h, LDAP_OPT_PROTOCOL_VERSION, 3); if (!$h || !@ldap_bind($h)) { $this->markTestSkipped('No server is listening on LDAP_HOST:LDAP_PORT'); diff --git a/src/Symfony/Component/Ldap/phpunit.xml.dist b/src/Symfony/Component/Ldap/phpunit.xml.dist index e9861f822170b..a679848078856 100644 --- a/src/Symfony/Component/Ldap/phpunit.xml.dist +++ b/src/Symfony/Component/Ldap/phpunit.xml.dist @@ -10,7 +10,7 @@ > - + diff --git a/src/Symfony/Component/Lock/Store/MemcachedStore.php b/src/Symfony/Component/Lock/Store/MemcachedStore.php index 2f8fd45586e61..e844a35bb6cfc 100644 --- a/src/Symfony/Component/Lock/Store/MemcachedStore.php +++ b/src/Symfony/Component/Lock/Store/MemcachedStore.php @@ -82,7 +82,7 @@ public function putOffExpiration(Key $key, float $ttl) $token = $this->getUniqueToken($key); - list($value, $cas) = $this->getValueAndCas($key); + [$value, $cas] = $this->getValueAndCas($key); $key->reduceLifetime($ttl); // Could happens when we ask a putOff after a timeout but in luck nobody steal the lock @@ -114,7 +114,7 @@ public function delete(Key $key) { $token = $this->getUniqueToken($key); - list($value, $cas) = $this->getValueAndCas($key); + [$value, $cas] = $this->getValueAndCas($key); if ($value !== $token) { // we are not the owner of the lock. Nothing to do. diff --git a/src/Symfony/Component/Lock/Tests/Store/SemaphoreStoreTest.php b/src/Symfony/Component/Lock/Tests/Store/SemaphoreStoreTest.php index 0611e2802c07c..782151a9d0539 100644 --- a/src/Symfony/Component/Lock/Tests/Store/SemaphoreStoreTest.php +++ b/src/Symfony/Component/Lock/Tests/Store/SemaphoreStoreTest.php @@ -60,7 +60,7 @@ private function getOpenedSemaphores() if ('------ Semaphore Status --------' !== $lines[0]) { throw new \Exception('Failed to extract list of opened semaphores. Expected a Semaphore status, got '.implode(\PHP_EOL, $lines)); } - list($key, $value) = explode(' = ', $lines[1]); + [$key, $value] = explode(' = ', $lines[1]); if ('used arrays' !== $key) { throw new \Exception('Failed to extract list of opened semaphores. Expected a "used arrays" key, got '.implode(\PHP_EOL, $lines)); } diff --git a/src/Symfony/Component/Mailer/Transport.php b/src/Symfony/Component/Mailer/Transport.php index 5d5c7e0b6eb8c..50801a15df085 100644 --- a/src/Symfony/Component/Mailer/Transport.php +++ b/src/Symfony/Component/Mailer/Transport.php @@ -83,7 +83,7 @@ public function fromStrings(array $dsns): Transports public function fromString(string $dsn): TransportInterface { - list($transport, $offset) = $this->parseDsn($dsn); + [$transport, $offset] = $this->parseDsn($dsn); if ($offset !== \strlen($dsn)) { throw new InvalidArgumentException(sprintf('The DSN has some garbage at the end: "%s".', substr($dsn, $offset))); } @@ -111,7 +111,7 @@ private function parseDsn(string $dsn, int $offset = 0): array ++$offset; $args = []; while (true) { - list($arg, $offset) = $this->parseDsn($dsn, $offset); + [$arg, $offset] = $this->parseDsn($dsn, $offset); $args[] = $arg; if (\strlen($dsn) === $offset) { break; diff --git a/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php b/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php index 365bcd1f8ba9a..5ef8a8825509f 100644 --- a/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php +++ b/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php @@ -290,7 +290,7 @@ private function assertResponseCode(string $response, array $codes): void throw new TransportException(sprintf('Expected response code "%s" but got an empty response.', implode('/', $codes))); } - list($code) = sscanf($response, '%3d'); + [$code] = sscanf($response, '%3d'); $valid = \in_array($code, $codes); if (!$valid) { diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrineIntegrationTest.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrineIntegrationTest.php index 470d20703d6b0..e99bf76f7733b 100644 --- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrineIntegrationTest.php +++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrineIntegrationTest.php @@ -62,7 +62,7 @@ public function testSendWithDelay() ->select('m.available_at') ->from('messenger_messages', 'm') ->where('m.body = :body') - ->setParameter(':body', '{"message": "Hi i am delayed"}') + ->setParameter('body', '{"message": "Hi i am delayed"}') ->execute(); $available_at = new \DateTime($stmt instanceof Result || $stmt instanceof DriverResult ? $stmt->fetchOne() : $stmt->fetchColumn()); diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrineReceiverTest.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrineReceiverTest.php index 6440cf0fe3c79..6e774ec71935e 100644 --- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrineReceiverTest.php +++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrineReceiverTest.php @@ -14,6 +14,7 @@ use Doctrine\DBAL\Driver\PDO\Exception; use Doctrine\DBAL\Driver\PDOException; use Doctrine\DBAL\Exception\DeadlockException; +use Doctrine\DBAL\Version; use PHPUnit\Framework\TestCase; use Symfony\Component\Messenger\Bridge\Doctrine\Tests\Fixtures\DummyMessage; use Symfony\Component\Messenger\Bridge\Doctrine\Transport\Connection; @@ -77,7 +78,14 @@ public function testOccursRetryableExceptionFromConnection() $serializer = $this->createSerializer(); $connection = $this->createMock(Connection::class); $driverException = class_exists(Exception::class) ? Exception::new(new \PDOException('Deadlock', 40001)) : new PDOException(new \PDOException('Deadlock', 40001)); - $connection->method('get')->willThrowException(new DeadlockException('Deadlock', $driverException)); + if (!class_exists(Version::class)) { + // This is doctrine/dbal 3.x + $deadlockException = new DeadlockException($driverException, null); + } else { + $deadlockException = new DeadlockException('Deadlock', $driverException); + } + + $connection->method('get')->willThrowException($deadlockException); $receiver = new DoctrineReceiver($connection, $serializer); $this->assertSame([], $receiver->get()); $this->assertSame([], $receiver->get()); diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php index ba095f288cd3b..d2bb1381ca69f 100644 --- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php +++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php @@ -16,6 +16,7 @@ use Doctrine\DBAL\Driver\Result as DriverResult; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Exception\TableNotFoundException; +use Doctrine\DBAL\LockMode; use Doctrine\DBAL\Query\QueryBuilder; use Doctrine\DBAL\Result; use Doctrine\DBAL\Schema\Comparator; @@ -172,9 +173,23 @@ public function get(): ?array ->orderBy('available_at', 'ASC') ->setMaxResults(1); + // Append pessimistic write lock to FROM clause if db platform supports it + $sql = $query->getSQL(); + if (($fromPart = $query->getQueryPart('from')) && + ($table = $fromPart[0]['table'] ?? null) && + ($alias = $fromPart[0]['alias'] ?? null) + ) { + $fromClause = sprintf('%s %s', $table, $alias); + $sql = str_replace( + sprintf('FROM %s WHERE', $fromClause), + sprintf('FROM %s WHERE', $this->driverConnection->getDatabasePlatform()->appendLockHint($fromClause, LockMode::PESSIMISTIC_WRITE)), + $sql + ); + } + // use SELECT ... FOR UPDATE to lock table $stmt = $this->executeQuery( - $query->getSQL().' '.$this->driverConnection->getDatabasePlatform()->getWriteLockSQL(), + $sql.' '.$this->driverConnection->getDatabasePlatform()->getWriteLockSQL(), $query->getParameters(), $query->getParameterTypes() ); @@ -372,7 +387,7 @@ private function executeQuery(string $sql, array $parameters = [], array $types return $stmt; } - private function executeStatement(string $sql, array $parameters = [], array $types = []) + protected function executeStatement(string $sql, array $parameters = [], array $types = []) { try { if (method_exists($this->driverConnection, 'executeStatement')) { diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/PostgreSqlConnection.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/PostgreSqlConnection.php index 79a1dd68ba411..9dc8b77e38756 100644 --- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/PostgreSqlConnection.php +++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/PostgreSqlConnection.php @@ -64,7 +64,7 @@ public function get(): ?array if (!$this->listening) { // This is secure because the table name must be a valid identifier: // https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS - $this->driverConnection->exec(sprintf('LISTEN "%s"', $this->configuration['table_name'])); + $this->executeStatement(sprintf('LISTEN "%s"', $this->configuration['table_name'])); $this->listening = true; } @@ -87,7 +87,7 @@ public function setup(): void { parent::setup(); - $this->driverConnection->exec(implode("\n", $this->getTriggerSql())); + $this->executeStatement(implode("\n", $this->getTriggerSql())); } /** @@ -134,7 +134,7 @@ private function unlisten() return; } - $this->driverConnection->exec(sprintf('UNLISTEN "%s"', $this->configuration['table_name'])); + $this->executeStatement(sprintf('UNLISTEN "%s"', $this->configuration['table_name'])); $this->listening = false; } } diff --git a/src/Symfony/Component/Mime/Email.php b/src/Symfony/Component/Mime/Email.php index e5f9f11b36fc4..c00c412e24b43 100644 --- a/src/Symfony/Component/Mime/Email.php +++ b/src/Symfony/Component/Mime/Email.php @@ -266,7 +266,7 @@ public function priority(int $priority) */ public function getPriority(): int { - list($priority) = sscanf($this->getHeaders()->getHeaderBody('X-Priority'), '%[1-5]'); + [$priority] = sscanf($this->getHeaders()->getHeaderBody('X-Priority'), '%[1-5]'); return $priority ?? 3; } diff --git a/src/Symfony/Component/Mime/Part/DataPart.php b/src/Symfony/Component/Mime/Part/DataPart.php index 5d1d91061b35f..213f3c10c8826 100644 --- a/src/Symfony/Component/Mime/Part/DataPart.php +++ b/src/Symfony/Component/Mime/Part/DataPart.php @@ -35,7 +35,7 @@ public function __construct($body, string $filename = null, string $contentType if (null === $contentType) { $contentType = 'application/octet-stream'; } - list($this->mediaType, $subtype) = explode('/', $contentType); + [$this->mediaType, $subtype] = explode('/', $contentType); parent::__construct($body, null, $subtype, $encoding); diff --git a/src/Symfony/Component/Process/Tests/ErrorProcessInitiator.php b/src/Symfony/Component/Process/Tests/ErrorProcessInitiator.php index 37c1e65846fd1..ae90c6518626d 100755 --- a/src/Symfony/Component/Process/Tests/ErrorProcessInitiator.php +++ b/src/Symfony/Component/Process/Tests/ErrorProcessInitiator.php @@ -16,7 +16,7 @@ require \dirname(__DIR__).'/vendor/autoload.php'; -list('e' => $php) = getopt('e:') + ['e' => 'php']; +['e' => $php] = getopt('e:') + ['e' => 'php']; try { $process = new Process("exec $php -r \"echo 'ready'; trigger_error('error', E_USER_ERROR);\""); diff --git a/src/Symfony/Component/Process/Tests/ProcessTest.php b/src/Symfony/Component/Process/Tests/ProcessTest.php index 6b525d26846f0..62bffec88f59c 100644 --- a/src/Symfony/Component/Process/Tests/ProcessTest.php +++ b/src/Symfony/Component/Process/Tests/ProcessTest.php @@ -476,6 +476,10 @@ public function testTTYCommand() $this->markTestSkipped('Windows does not have /dev/tty support'); } + if (!Process::isTtySupported()) { + $this->markTestSkipped('There is no TTY support'); + } + $process = $this->getProcess('echo "foo" >> /dev/null && '.$this->getProcessForCode('usleep(100000);')->getCommandLine()); $process->setTty(true); $process->start(); @@ -491,6 +495,10 @@ public function testTTYCommandExitCode() $this->markTestSkipped('Windows does have /dev/tty support'); } + if (!Process::isTtySupported()) { + $this->markTestSkipped('There is no TTY support'); + } + $process = $this->getProcess('echo "foo" >> /dev/null'); $process->setTty(true); $process->run(); @@ -1433,16 +1441,7 @@ public function testRawCommandLine() $p = Process::fromShellCommandline(sprintf('"%s" -r %s "a" "" "b"', self::$phpBin, escapeshellarg('print_r($argv);'))); $p->run(); - $expected = << - - [1] => a - [2] => - [3] => b -) - -EOTXT; + $expected = "Array\n(\n [0] => -\n [1] => a\n [2] => \n [3] => b\n)\n"; $this->assertSame($expected, str_replace('Standard input code', '-', $p->getOutput())); } diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 835793ccbe12e..cb4051d3a069e 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -209,7 +209,7 @@ private static function throwInvalidArgumentException(string $message, array $tr } if (preg_match('/^\S+::\S+\(\): Argument #\d+ \(\$\S+\) must be of type (\S+), (\S+) given/', $message, $matches)) { - list(, $expectedType, $actualType) = $matches; + [, $expectedType, $actualType] = $matches; throw new InvalidArgumentException(sprintf('Expected argument of type "%s", "%s" given at property path "%s".', $expectedType, 'NULL' === $actualType ? 'null' : $actualType, $propertyPath), 0, $previous); } @@ -404,7 +404,7 @@ private function readProperty(array $zval, string $property, bool $ignoreInvalid try { $result[self::VALUE] = $object->$name(); } catch (\TypeError $e) { - list($trace) = $e->getTrace(); + [$trace] = $e->getTrace(); // handle uninitialized properties in PHP >= 7 if (__FILE__ === $trace['file'] diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyPathBuilderTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyPathBuilderTest.php index 1ae8fdd31d31d..b121a48087df7 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyPathBuilderTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyPathBuilderTest.php @@ -20,7 +20,7 @@ */ class PropertyPathBuilderTest extends TestCase { - const PREFIX = 'old1[old2].old3[old4][old5].old6'; + private const PREFIX = 'old1[old2].old3[old4][old5].old6'; /** * @var PropertyPathBuilder diff --git a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php index 9a64428c98b89..7141d7ba2c97b 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php @@ -77,7 +77,7 @@ public function __construct(DocBlockFactoryInterface $docBlockFactory = null, ar public function getShortDescription(string $class, string $property, array $context = []): ?string { /** @var $docBlock DocBlock */ - list($docBlock) = $this->getDocBlock($class, $property); + [$docBlock] = $this->getDocBlock($class, $property); if (!$docBlock) { return null; } @@ -107,7 +107,7 @@ public function getShortDescription(string $class, string $property, array $cont public function getLongDescription(string $class, string $property, array $context = []): ?string { /** @var $docBlock DocBlock */ - list($docBlock) = $this->getDocBlock($class, $property); + [$docBlock] = $this->getDocBlock($class, $property); if (!$docBlock) { return null; } @@ -123,7 +123,7 @@ public function getLongDescription(string $class, string $property, array $conte public function getTypes(string $class, string $property, array $context = []): ?array { /** @var $docBlock DocBlock */ - list($docBlock, $source, $prefix) = $this->getDocBlock($class, $property); + [$docBlock, $source, $prefix] = $this->getDocBlock($class, $property); if (!$docBlock) { return null; } @@ -176,11 +176,11 @@ private function getDocBlock(string $class, string $property): array $data = [$docBlock, self::PROPERTY, null]; break; - case list($docBlock) = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::ACCESSOR): + case [$docBlock] = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::ACCESSOR): $data = [$docBlock, self::ACCESSOR, null]; break; - case list($docBlock, $prefix) = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::MUTATOR): + case [$docBlock, $prefix] = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::MUTATOR): $data = [$docBlock, self::MUTATOR, $prefix]; break; diff --git a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php index 710940812191c..a4725ba3ceec5 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php @@ -185,7 +185,7 @@ public function isWritable(string $class, string $property, array $context = []) return true; } - list($reflectionMethod) = $this->getMutatorMethod($class, $property); + [$reflectionMethod] = $this->getMutatorMethod($class, $property); return null !== $reflectionMethod; } @@ -384,7 +384,7 @@ public function getWriteInfo(string $class, string $property, array $context = [ */ private function extractFromMutator(string $class, string $property): ?array { - list($reflectionMethod, $prefix) = $this->getMutatorMethod($class, $property); + [$reflectionMethod, $prefix] = $this->getMutatorMethod($class, $property); if (null === $reflectionMethod) { return null; } @@ -411,7 +411,7 @@ private function extractFromMutator(string $class, string $property): ?array */ private function extractFromAccessor(string $class, string $property): ?array { - list($reflectionMethod, $prefix) = $this->getAccessorMethod($class, $property); + [$reflectionMethod, $prefix] = $this->getAccessorMethod($class, $property); if (null === $reflectionMethod) { return null; } diff --git a/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php b/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php index e7b06f1e32b39..06b7489201217 100644 --- a/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php +++ b/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php @@ -90,7 +90,7 @@ private function createType(DocType $type, bool $nullable, string $docType = nul $docType = $docType ?? (string) $type; if ($type instanceof Collection) { - list($phpType, $class) = $this->getPhpTypeAndClass((string) $type->getFqsen()); + [$phpType, $class] = $this->getPhpTypeAndClass((string) $type->getFqsen()); $key = $this->getTypes($type->getKeyType()); $value = $this->getTypes($type->getValueType()); @@ -121,7 +121,7 @@ private function createType(DocType $type, bool $nullable, string $docType = nul } $docType = $this->normalizeType($docType); - list($phpType, $class) = $this->getPhpTypeAndClass($docType); + [$phpType, $class] = $this->getPhpTypeAndClass($docType); if ('array' === $docType) { return new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true, null, null); diff --git a/src/Symfony/Component/Routing/Generator/CompiledUrlGenerator.php b/src/Symfony/Component/Routing/Generator/CompiledUrlGenerator.php index b10edce4e9249..90ce5b2906880 100644 --- a/src/Symfony/Component/Routing/Generator/CompiledUrlGenerator.php +++ b/src/Symfony/Component/Routing/Generator/CompiledUrlGenerator.php @@ -50,7 +50,7 @@ public function generate(string $name, array $parameters = [], int $referenceTyp throw new RouteNotFoundException(sprintf('Unable to generate a URL for the named route "%s" as such route does not exist.', $name)); } - list($variables, $defaults, $requirements, $tokens, $hostTokens, $requiredSchemes) = $this->compiledRoutes[$name]; + [$variables, $defaults, $requirements, $tokens, $hostTokens, $requiredSchemes] = $this->compiledRoutes[$name]; if (isset($defaults['_canonical_route']) && isset($defaults['_locale'])) { if (!\in_array('_locale', $variables, true)) { diff --git a/src/Symfony/Component/Routing/Loader/XmlFileLoader.php b/src/Symfony/Component/Routing/Loader/XmlFileLoader.php index e64089fb30a9a..dbff4a87b37c0 100644 --- a/src/Symfony/Component/Routing/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/Routing/Loader/XmlFileLoader.php @@ -118,7 +118,7 @@ protected function parseRoute(RouteCollection $collection, \DOMElement $node, st $schemes = preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, \PREG_SPLIT_NO_EMPTY); $methods = preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, \PREG_SPLIT_NO_EMPTY); - list($defaults, $requirements, $options, $condition, $paths, /* $prefixes */, $hosts) = $this->parseConfigs($node, $path); + [$defaults, $requirements, $options, $condition, $paths, /* $prefixes */, $hosts] = $this->parseConfigs($node, $path); if (!$paths && '' === $node->getAttribute('path')) { throw new \InvalidArgumentException(sprintf('The element in file "%s" must have a "path" attribute or child nodes.', $path)); @@ -163,7 +163,7 @@ protected function parseImport(RouteCollection $collection, \DOMElement $node, s $trailingSlashOnRoot = $node->hasAttribute('trailing-slash-on-root') ? XmlUtils::phpize($node->getAttribute('trailing-slash-on-root')) : true; $namePrefix = $node->getAttribute('name-prefix') ?: null; - list($defaults, $requirements, $options, $condition, /* $paths */, $prefixes, $hosts) = $this->parseConfigs($node, $path); + [$defaults, $requirements, $options, $condition, /* $paths */, $prefixes, $hosts] = $this->parseConfigs($node, $path); if ('' !== $prefix && $prefixes) { throw new \InvalidArgumentException(sprintf('The element in file "%s" must not have both a "prefix" attribute and child nodes.', $path)); diff --git a/src/Symfony/Component/Routing/Matcher/CompiledUrlMatcher.php b/src/Symfony/Component/Routing/Matcher/CompiledUrlMatcher.php index e15cda7786585..ae13fd7011e37 100644 --- a/src/Symfony/Component/Routing/Matcher/CompiledUrlMatcher.php +++ b/src/Symfony/Component/Routing/Matcher/CompiledUrlMatcher.php @@ -26,6 +26,6 @@ class CompiledUrlMatcher extends UrlMatcher public function __construct(array $compiledRoutes, RequestContext $context) { $this->context = $context; - list($this->matchHost, $this->staticRoutes, $this->regexpList, $this->dynamicRoutes, $this->checkCondition) = $compiledRoutes; + [$this->matchHost, $this->staticRoutes, $this->regexpList, $this->dynamicRoutes, $this->checkCondition] = $compiledRoutes; } } diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/CompiledUrlMatcherDumper.php b/src/Symfony/Component/Routing/Matcher/Dumper/CompiledUrlMatcherDumper.php index 73e2e1e0a06af..402ac51351d55 100644 --- a/src/Symfony/Component/Routing/Matcher/Dumper/CompiledUrlMatcherDumper.php +++ b/src/Symfony/Component/Routing/Matcher/Dumper/CompiledUrlMatcherDumper.php @@ -83,7 +83,7 @@ public function getCompiledRoutes(bool $forDump = false): array $routes = $this->getRoutes(); } - list($staticRoutes, $dynamicRoutes) = $this->groupStaticRoutes($routes); + [$staticRoutes, $dynamicRoutes] = $this->groupStaticRoutes($routes); $conditions = [null]; $compiledRoutes[] = $this->compileStaticRoutes($staticRoutes, $conditions); @@ -131,7 +131,7 @@ static function (\$condition, \$context, \$request) { // \$checkCondition private function generateCompiledRoutes(): string { - list($matchHost, $staticRoutes, $regexpCode, $dynamicRoutes, $checkConditionCode) = $this->getCompiledRoutes(true); + [$matchHost, $staticRoutes, $regexpCode, $dynamicRoutes, $checkConditionCode] = $this->getCompiledRoutes(true); $code = self::export($matchHost).', // $matchHost'."\n"; @@ -186,7 +186,7 @@ private function groupStaticRoutes(RouteCollection $collection): array if ($hasTrailingSlash) { $url = substr($url, 0, -1); } - foreach ($dynamicRegex as list($hostRx, $rx, $prefix)) { + foreach ($dynamicRegex as [$hostRx, $rx, $prefix]) { if (('' === $prefix || 0 === strpos($url, $prefix)) && (preg_match($rx, $url) || preg_match($rx, $url.'/')) && (!$host || !$hostRx || preg_match($hostRx, $host))) { $dynamicRegex[] = [$hostRegex, $regex, $staticPrefix]; $dynamicRoutes->add($name, $route); @@ -221,7 +221,7 @@ private function compileStaticRoutes(array $staticRoutes, array &$conditions): a foreach ($staticRoutes as $url => $routes) { $compiledRoutes[$url] = []; - foreach ($routes as $name => list($route, $hasTrailingSlash)) { + foreach ($routes as $name => [$route, $hasTrailingSlash]) { $compiledRoutes[$url][] = $this->compileRoute($route, $name, (!$route->compile()->getHostVariables() ? $route->getHost() : $route->compile()->getHostRegex()) ?: null, $hasTrailingSlash, false, $conditions); } } @@ -242,7 +242,7 @@ private function compileStaticRoutes(array $staticRoutes, array &$conditions): a * Paths that can match two or more routes, or have user-specified conditions are put in separate switch's cases. * * Last but not least: - * - Because it is not possibe to mix unicode/non-unicode patterns in a single regexp, several of them can be generated. + * - Because it is not possible to mix unicode/non-unicode patterns in a single regexp, several of them can be generated. * - The same regexp can be used several times when the logic in the switch rejects the match. When this happens, the * matching-but-failing subpattern is excluded by replacing its name by "(*F)", which forces a failure-to-match. * To ease this backlisting operation, the name of subpatterns is also the string offset where the replacement should occur. @@ -287,7 +287,7 @@ private function compileDynamicRoutes(RouteCollection $collection, bool $matchHo $routes->add($name, $route); } - foreach ($perModifiers as list($modifiers, $routes)) { + foreach ($perModifiers as [$modifiers, $routes]) { $prev = false; $perHost = []; foreach ($routes->all() as $name => $route) { @@ -306,7 +306,7 @@ private function compileDynamicRoutes(RouteCollection $collection, bool $matchHo $state->mark += \strlen($rx); $state->regex = $rx; - foreach ($perHost as list($hostRegex, $routes)) { + foreach ($perHost as [$hostRegex, $routes]) { if ($matchHost) { if ($hostRegex) { preg_match('#^.\^(.*)\$.[a-zA-Z]*$#', $hostRegex, $rx); @@ -391,7 +391,7 @@ private function compileStaticPrefixCollection(StaticPrefixCollection $tree, \st continue; } - list($name, $regex, $vars, $route, $hasTrailingSlash, $hasTrailingVar) = $route; + [$name, $regex, $vars, $route, $hasTrailingSlash, $hasTrailingVar] = $route; $compiledRoute = $route->compile(); $vars = array_merge($state->hostVars, $vars); diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/CompiledUrlMatcherTrait.php b/src/Symfony/Component/Routing/Matcher/Dumper/CompiledUrlMatcherTrait.php index caba4e5c6287c..79cdc2b48ab61 100644 --- a/src/Symfony/Component/Routing/Matcher/Dumper/CompiledUrlMatcherTrait.php +++ b/src/Symfony/Component/Routing/Matcher/Dumper/CompiledUrlMatcherTrait.php @@ -87,7 +87,7 @@ private function doMatch(string $pathinfo, array &$allow = [], array &$allowSche } $supportsRedirections = 'GET' === $canonicalMethod && $this instanceof RedirectableUrlMatcherInterface; - foreach ($this->staticRoutes[$trimmedPathinfo] ?? [] as list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash, , $condition)) { + foreach ($this->staticRoutes[$trimmedPathinfo] ?? [] as [$ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash, , $condition]) { if ($condition && !($this->checkCondition)($condition, $context, 0 < $condition ? $request ?? $request = $this->request ?: $this->createRequest($pathinfo) : null)) { continue; } @@ -127,7 +127,7 @@ private function doMatch(string $pathinfo, array &$allow = [], array &$allowSche foreach ($this->regexpList as $offset => $regex) { while (preg_match($regex, $matchedPathinfo, $matches)) { - foreach ($this->dynamicRoutes[$m = (int) $matches['MARK']] as list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash, $hasTrailingVar, $condition)) { + foreach ($this->dynamicRoutes[$m = (int) $matches['MARK']] as [$ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash, $hasTrailingVar, $condition]) { if (null !== $condition) { if (0 === $condition) { // marks the last route in the regexp continue 3; diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/StaticPrefixCollection.php b/src/Symfony/Component/Routing/Matcher/Dumper/StaticPrefixCollection.php index 65b6c0718b316..1c5c5fdeb0c99 100644 --- a/src/Symfony/Component/Routing/Matcher/Dumper/StaticPrefixCollection.php +++ b/src/Symfony/Component/Routing/Matcher/Dumper/StaticPrefixCollection.php @@ -65,12 +65,12 @@ public function getRoutes(): array */ public function addRoute(string $prefix, $route) { - list($prefix, $staticPrefix) = $this->getCommonPrefix($prefix, $prefix); + [$prefix, $staticPrefix] = $this->getCommonPrefix($prefix, $prefix); for ($i = \count($this->items) - 1; 0 <= $i; --$i) { $item = $this->items[$i]; - list($commonPrefix, $commonStaticPrefix) = $this->getCommonPrefix($prefix, $this->prefixes[$i]); + [$commonPrefix, $commonStaticPrefix] = $this->getCommonPrefix($prefix, $this->prefixes[$i]); if ($this->prefix === $commonPrefix) { // the new route and a previous one have no common prefix, let's see if they are exclusive to each others @@ -104,8 +104,8 @@ public function addRoute(string $prefix, $route) } else { // the new route and a previous one have a common prefix, let's merge them $child = new self($commonPrefix); - list($child->prefixes[0], $child->staticPrefixes[0]) = $child->getCommonPrefix($this->prefixes[$i], $this->prefixes[$i]); - list($child->prefixes[1], $child->staticPrefixes[1]) = $child->getCommonPrefix($prefix, $prefix); + [$child->prefixes[0], $child->staticPrefixes[0]] = $child->getCommonPrefix($this->prefixes[$i], $this->prefixes[$i]); + [$child->prefixes[1], $child->staticPrefixes[1]] = $child->getCommonPrefix($prefix, $prefix); $child->items = [$this->items[$i], $route]; $this->staticPrefixes[$i] = $commonStaticPrefix; diff --git a/src/Symfony/Component/Routing/Tests/Matcher/Dumper/StaticPrefixCollectionTest.php b/src/Symfony/Component/Routing/Tests/Matcher/Dumper/StaticPrefixCollectionTest.php index fe5014fc80007..36b2756690cdb 100644 --- a/src/Symfony/Component/Routing/Tests/Matcher/Dumper/StaticPrefixCollectionTest.php +++ b/src/Symfony/Component/Routing/Tests/Matcher/Dumper/StaticPrefixCollectionTest.php @@ -16,7 +16,7 @@ public function testGrouping(array $routes, $expected) $collection = new StaticPrefixCollection('/'); foreach ($routes as $route) { - list($path, $name) = $route; + [$path, $name] = $route; $staticPrefix = (new Route($path))->compile()->getStaticPrefix(); $collection->addRoute($staticPrefix, [$name]); } diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/Storage/TokenStorageInterface.php b/src/Symfony/Component/Security/Core/Authentication/Token/Storage/TokenStorageInterface.php index 218d750b8e097..779109039bfe6 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Token/Storage/TokenStorageInterface.php +++ b/src/Symfony/Component/Security/Core/Authentication/Token/Storage/TokenStorageInterface.php @@ -30,7 +30,7 @@ public function getToken(); /** * Sets the authentication token. * - * @param TokenInterface $token A TokenInterface token, or null if no further authentication information should be stored + * @param TokenInterface|null $token A TokenInterface token, or null if no further authentication information should be stored */ public function setToken(TokenInterface $token = null); } diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.af.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.af.xlf new file mode 100644 index 0000000000000..4fc8b1426e381 --- /dev/null +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.af.xlf @@ -0,0 +1,75 @@ + + + + + + An authentication exception occurred. + 'n Verifikasie probleem het voorgekom. + + + Authentication credentials could not be found. + Verifikasiebewyse kon nie gevind word nie. + + + Authentication request could not be processed due to a system problem. + Verifikasieversoek kon weens 'n stelselprobleem nie verwerk word nie. + + + Invalid credentials. + Ongedige verifikasiebewyse. + + + Cookie has already been used by someone else. + Die koekie is alreeds deur iemand anders gebruik. + + + Not privileged to request the resource. + Nie bevoorreg om die hulpbron aan te vra nie. + + + Invalid CSRF token. + Ongeldige CSRF-teken. + + + No authentication provider found to support the authentication token. + Geen verifikasieverskaffer is gevind wat die verifikasietoken kan ondersteun nie. + + + No session available, it either timed out or cookies are not enabled. + Geen sessie is beskikbaar, die het verval of koekies is nie geaktiveer nie. + + + No token could be found. + Geen teken kon gevind word nie. + + + Username could not be found. + Gebruikersnaam kon nie gevind word nie. + + + Account has expired. + Rekening het verval. + + + Credentials have expired. + Verifikasiebewyse het verval. + + + Account is disabled. + Rekening is deaktiveer. + + + Account is locked. + Rekening is gesluit. + + + Too many failed login attempts, please try again later. + Te veel mislukte aanmeldpogings, probeer asseblief later weer. + + + Invalid or expired login link. + Ongeldige of vervalde aanmeldskakel. + + + + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.az.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.az.xlf index d9d5425cefb9c..735de0786567c 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.az.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.az.xlf @@ -62,6 +62,14 @@ Account is locked. Hesab kilitlənib. + + Too many failed login attempts, please try again later. + Çoxlu uğursuz giriş təşəbbüsü, zəhmət olmasa daha sonra yeniden yoxlayın. + + + Invalid or expired login link. + Yanlış və ya müddəti keçmiş giriş keçidi. + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.bs.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.bs.xlf new file mode 100644 index 0000000000000..2eae0ff22ec62 --- /dev/null +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.bs.xlf @@ -0,0 +1,75 @@ + + + + + + An authentication exception occurred. + Došlo je do autentifikacijskog izuzetka (exception). + + + Authentication credentials could not be found. + Autentifikacijski podaci nisu pronađeni. + + + Authentication request could not be processed due to a system problem. + Autentifikacijski zahtjev ne može biti obrađen zbog sistemskog problema. + + + Invalid credentials. + Autentifikacijski podaci su neispravni. + + + Cookie has already been used by someone else. + Neko drugi je već iskoristio ovaj kolačić (cookie). + + + Not privileged to request the resource. + Nemate privilegije potrebne za pristup ovom resursu. + + + Invalid CSRF token. + CSRF žeton (token) je neispravan. + + + No authentication provider found to support the authentication token. + Nije pronađen autentifikacijski provajder koji bi podržao dati autentifikacijski žeton (token). + + + No session available, it either timed out or cookies are not enabled. + Nema dostupnih sesija; ili je istekla ili su kolačići (cookies) iksljučeni. + + + No token could be found. + Nije pronađen nijedan žeton (token). + + + Username could not be found. + Korisničko ime nije pronađeno. + + + Account has expired. + Nalog je istekao. + + + Credentials have expired. + Autentifikacijski podaci su istekli. + + + Account is disabled. + Nalog je onemogućen. + + + Account is locked. + Nalog je zaključan. + + + Too many failed login attempts, please try again later. + Previše neuspješnih pokušaja prijavljivanja, molim pokušajte ponovo kasnije. + + + Invalid or expired login link. + Link za prijavljivanje je istekao ili je neispravan. + + + + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.ca.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.ca.xlf index b009c6205c362..5a8d0c7d84880 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.ca.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.ca.xlf @@ -62,6 +62,14 @@ Account is locked. El compte està bloquejat. + + Too many failed login attempts, please try again later. + Massa intents d'inici de sessió fallits, torneu-ho a provar més tard. + + + Invalid or expired login link. + Enllaç d'inici de sessió no vàlid o caducat. + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.da.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.da.xlf index 183ebe83e71dd..c83d27c5e0b59 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.da.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.da.xlf @@ -8,7 +8,7 @@ Authentication credentials could not be found. - Loginoplysninger kan findes. + Loginoplysninger kan ikke findes. Authentication request could not be processed due to a system problem. @@ -62,6 +62,14 @@ Account is locked. Brugerkonto er låst. + + Too many failed login attempts, please try again later. + For mange fejlede login forsøg, prøv venligst senere. + + + Invalid or expired login link. + Ugyldigt eller udløbet login link. + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.el.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.el.xlf index 02393d0805252..724cc4e5d1603 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.el.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.el.xlf @@ -62,6 +62,14 @@ Account is locked. Ο λογαριασμός είναι κλειδωμένος. + + Too many failed login attempts, please try again later. + Πολλαπλές αποτυχημένες απόπειρες σύνδεσης, παρακαλούμε ξαναδοκιμάστε αργότερα. + + + Invalid or expired login link. + Μη έγκυρος ή ληγμένος σύνδεσμος σύνδεσης. + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.fi.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.fi.xlf index d5358a6c1700c..7b8d232cdd9a9 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.fi.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.fi.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Virheellinen CSRF tunnus. - - Digest nonce has expired. - Digest nonce on vanhentunut. - No authentication provider found to support the authentication token. Autentikointi tunnukselle ei löydetty tuettua autentikointi tarjoajaa. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.gl.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.gl.xlf index ddc838e66af8b..651810d452cb6 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.gl.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.gl.xlf @@ -62,6 +62,14 @@ Account is locked. A conta está bloqueada. + + Too many failed login attempts, please try again later. + Demasiados intentos de inicio de sesión fallados. Téntao de novo máis tarde. + + + Invalid or expired login link. + Ligazón de inicio de sesión non válida ou caducada. + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.he.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.he.xlf index f30b68eddf1fc..adbb6cfd6ae00 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.he.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.he.xlf @@ -8,7 +8,7 @@ Authentication credentials could not be found. - Authentication credentials could not be found. + פרטי זיהוי לא נמצאו. Authentication request could not be processed due to a system problem. @@ -16,23 +16,23 @@ Invalid credentials. - שם משתמש או סיסמא שגויים + שם משתמש או סיסמא שגויים. Cookie has already been used by someone else. - Cookie has already been used by someone else. + עוגיה כבר שומשה. Not privileged to request the resource. - Not privileged to request the resource. + אין הרשאה מתאימה. Invalid CSRF token. - Invalid CSRF token. + אסימון CSRF לא חוקי. No authentication provider found to support the authentication token. - No authentication provider found to support the authentication token. + לא נמצא ספק אימות המתאימה לבקשה. No session available, it either timed out or cookies are not enabled. @@ -40,11 +40,11 @@ No token could be found. - No token could be found. + הטוקן לא נמצא. Username could not be found. - שם משתמש לא נמצא במערכת + שם משתמש לא נמצא. Account has expired. @@ -52,7 +52,7 @@ Credentials have expired. - פרטי התחברות פקעו תוקף + פרטי התחברות פקעו תוקף. Account is disabled. @@ -62,6 +62,14 @@ Account is locked. החשבון נעול. + + Too many failed login attempts, please try again later. + יותר מדי ניסיונות כניסה כושלים, אנא נסה שוב מאוחר יותר. + + + Invalid or expired login link. + קישור כניסה לא חוקי או שפג תוקפו. + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.hu.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.hu.xlf index f3a163904d367..e27a7b4505c53 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.hu.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.hu.xlf @@ -62,6 +62,14 @@ Account is locked. Zárolt fiók. + + Too many failed login attempts, please try again later. + Túl sok sikertelen bejelentkezési kísérlet, kérjük próbálja újra később. + + + Invalid or expired login link. + Érvénytelen vagy lejárt bejelentkezési link. + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.lt.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.lt.xlf index b7909033a6e67..37487b79d71b4 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.lt.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.lt.xlf @@ -62,6 +62,14 @@ Account is locked. Paskyra yra užblokuota. + + Too many failed login attempts, please try again later. + Per daug nepavykusių prisijungimo bandymų, pabandykite dar kartą vėliau. + + + Invalid or expired login link. + Netinkama arba pasibaigusio galiojimo laiko prisijungimo nuoroda. + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.mn.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.mn.xlf new file mode 100644 index 0000000000000..7310e660a4479 --- /dev/null +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.mn.xlf @@ -0,0 +1,75 @@ + + + + + + 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. + Күүки файлыг аль хэдийн өөр хүн хэрэглэж байна. + + + Not privileged to request the resource. + Энэхүү мэдээллийг авах эрх хүрэхгүй байна. + + + Invalid CSRF token. + Тохиромжгүй CSRF токен. + + + No authentication provider found to support the authentication token. + Нэвтрэх токенг дэмжих нэвтрэх эрхийн хангагч олдсонгүй. + + + No available, it either timed out or cookies are not enabled. + Хэрэглэгчийн session олдсонгүй, хугацаа нь дууссан эсвэл күүки идэвхижүүлээгүй байна. + + + No token could be found. + Токен олдсонгүй. + + + Username could not be found. + Нэвтрэх нэр олсонгүй. + + + Account has expired. + Бүртгэлийн хугацаа дууссан байна. + + + Credentials have expired. + Нэвтрэх эрхийн хугацаа дууссан байна. + + + Account is disabled. + Бүртгэлийг хаасан байна. + + + Account is locked. + Бүртгэлийг цоожилсон байна. + + + Too many failed login attempts, please try again later. + Хэтэрхий олон амжилтгүй оролдлого, түр хүлээгээд дахин оролдоно уу. + + + Invalid or expired login link. + Буруу эсвэл хугацаа нь дууссан нэвтрэх зам. + + + + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.nb.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.nb.xlf index 57016a4a4c23f..2d3a87c793ddf 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.nb.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.nb.xlf @@ -62,6 +62,14 @@ Account is locked. Brukerkonto er sperret. + + Too many failed login attempts, please try again later. + For mange mislykkede påloggingsforsøk. Prøv igjen senere. + + + Invalid or expired login link. + Ugyldig eller utløpt påloggingskobling. + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.no.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.no.xlf index 57016a4a4c23f..2d3a87c793ddf 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.no.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.no.xlf @@ -62,6 +62,14 @@ Account is locked. Brukerkonto er sperret. + + Too many failed login attempts, please try again later. + For mange mislykkede påloggingsforsøk. Prøv igjen senere. + + + Invalid or expired login link. + Ugyldig eller utløpt påloggingskobling. + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.pt_PT.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.pt.xlf similarity index 100% rename from src/Symfony/Component/Security/Core/Resources/translations/security.pt_PT.xlf rename to src/Symfony/Component/Security/Core/Resources/translations/security.pt.xlf diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.ro.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.ro.xlf index f35a2bb815878..1462e650e9c4b 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.ro.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.ro.xlf @@ -62,6 +62,14 @@ Account is locked. Contul este blocat. + + Too many failed login attempts, please try again later. + Prea multe încercări de autentificare eșuate, vă rugăm să încercați mai târziu. + + + Invalid or expired login link. + Link de autentificare invalid sau expirat. + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.sq.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.sq.xlf index 4f4bc6d4cbc61..03e13708af4b9 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.sq.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.sq.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Identifikues i pavlefshëm CSRF. - - Digest nonce has expired. - Numeri një perdorimësh i verifikimit Digest ka skaduar. - No authentication provider found to support the authentication token. Asnjë ofrues i vërtetimit nuk u gjet që të mbështesë simbolin e vërtetimit. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.sr_Cyrl.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.sr_Cyrl.xlf index f677254cce202..92ba9004aa8b1 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.sr_Cyrl.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.sr_Cyrl.xlf @@ -62,6 +62,14 @@ Account is locked. Налог је закључан. + + Too many failed login attempts, please try again later. + Превише неуспешних покушаја пријављивања, молим покушајте поново касније. + + + Invalid or expired login link. + Линк за пријављивање је истекао или је неисправан. + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.sr_Latn.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.sr_Latn.xlf index a38c75a9f810f..219281d66ecc7 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.sr_Latn.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.sr_Latn.xlf @@ -62,6 +62,14 @@ Account is locked. Nalog je zaključan. + + Too many failed login attempts, please try again later. + Previše neuspešnih pokušaja prijavljivanja, molim pokušajte ponovo kasnije. + + + Invalid or expired login link. + Link za prijavljivanje je istekao ili je neispravan. + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.th.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.th.xlf index 84ae66769c611..2b2f1e068ba76 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.th.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.th.xlf @@ -62,6 +62,14 @@ Account is locked. บัญชีถูกล็อกแล้ว + + Too many failed login attempts, please try again later. + มีความพยายามเข้าสู่ระบบล้มเหลวมากเกินไป กรุณาลองใหม่ภายหลัง + + + Invalid or expired login link. + ลิงค์เข้าสู่ระบบไม่ถูกต้องหรือหมดอายุไปแล้ว + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.tl.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.tl.xlf index d80cf88de1c8a..5bf5304b5cae1 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.tl.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.tl.xlf @@ -48,19 +48,27 @@ Account has expired. - Ang akawnt ay nag-expire na. + Ang account ay nag-expire na. Credentials have expired. - .ng mga kinakailangang dokumento ay nag expire na. + Ang mga kinakailangang dokumento ay nag expire na. Account is disabled. - Ang akawnt ay hindi pinagana. + Ang account ay hindi pinagana. Account is locked. - ng akawnt ay nakasara. + Ang account ay nakasara. + + + Too many failed login attempts, please try again later. + Madaming bagsak na pagtatangka, pakisubukan ulit mamaya. + + + Invalid or expired login link. + Inbalido o nagexpire na ang link para makapaglogin. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.zh_TW.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.zh_TW.xlf index 7085206440528..86310473180b1 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.zh_TW.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.zh_TW.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. 無效的 CSRF token 。 - - Digest nonce has expired. - 摘要隨機串(digest nonce)已過期。 - No authentication provider found to support the authentication token. 沒有找到支持此 token 的身份驗證服務提供方。 diff --git a/src/Symfony/Component/Security/Core/Tests/Authorization/TraceableAccessDecisionManagerTest.php b/src/Symfony/Component/Security/Core/Tests/Authorization/TraceableAccessDecisionManagerTest.php index f9e157c6978e0..cad08eb9e8753 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authorization/TraceableAccessDecisionManagerTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authorization/TraceableAccessDecisionManagerTest.php @@ -40,7 +40,7 @@ public function testDecideLog(array $expectedLog, array $attributes, $object, ar ->with($token, $attributes, $object) ->willReturnCallback(function ($token, $attributes, $object) use ($voterVotes, $adm, $result) { foreach ($voterVotes as $voterVote) { - list($voter, $vote) = $voterVote; + [$voter, $vote] = $voterVote; $adm->addVoterVote($voter, $attributes, $vote); } diff --git a/src/Symfony/Component/Security/Core/Tests/Resources/TranslationFilesTest.php b/src/Symfony/Component/Security/Core/Tests/Resources/TranslationFilesTest.php index c45b5a0d36338..4255e91d926b8 100644 --- a/src/Symfony/Component/Security/Core/Tests/Resources/TranslationFilesTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Resources/TranslationFilesTest.php @@ -12,7 +12,7 @@ namespace Symfony\Component\Security\Core\Tests\Resources; use PHPUnit\Framework\TestCase; -use PHPUnit\Util\Xml\Loader; +use Symfony\Component\Translation\Util\XliffUtils; class TranslationFilesTest extends TestCase { @@ -21,13 +21,26 @@ class TranslationFilesTest extends TestCase */ public function testTranslationFileIsValid($filePath) { - $loader = class_exists(Loader::class) - ? [new Loader(), 'loadFile'] - : ['PHPUnit\Util\XML', 'loadfile']; + $document = new \DOMDocument(); + $document->loadXML(file_get_contents($filePath)); - $loader($filePath, false, false, true); + $errors = XliffUtils::validateSchema($document); - $this->addToAssertionCount(1); + $this->assertCount(0, $errors, sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message')))); + } + + /** + * @dataProvider provideTranslationFiles + */ + public function testTranslationFileIsValidWithoutEntityLoader($filePath) + { + $document = new \DOMDocument(); + $document->loadXML(file_get_contents($filePath)); + libxml_disable_entity_loader(true); + + $errors = XliffUtils::validateSchema($document); + + $this->assertCount(0, $errors, sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message')))); } public function provideTranslationFiles() diff --git a/src/Symfony/Component/Security/Core/Tests/Validator/Constraints/UserPasswordValidatorTest.php b/src/Symfony/Component/Security/Core/Tests/Validator/Constraints/UserPasswordValidatorTest.php index efdc8585f0a31..4e11c47f8ca7d 100644 --- a/src/Symfony/Component/Security/Core/Tests/Validator/Constraints/UserPasswordValidatorTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Validator/Constraints/UserPasswordValidatorTest.php @@ -23,8 +23,8 @@ */ abstract class UserPasswordValidatorTest extends ConstraintValidatorTestCase { - const PASSWORD = 's3Cr3t'; - const SALT = '^S4lt$'; + private const PASSWORD = 's3Cr3t'; + private const SALT = '^S4lt$'; /** * @var TokenStorageInterface diff --git a/src/Symfony/Component/Security/Core/composer.json b/src/Symfony/Component/Security/Core/composer.json index eccd6c1d3c92c..683e49d9b8b1a 100644 --- a/src/Symfony/Component/Security/Core/composer.json +++ b/src/Symfony/Component/Security/Core/composer.json @@ -28,6 +28,7 @@ "symfony/expression-language": "^4.4|^5.0", "symfony/http-foundation": "^4.4|^5.0", "symfony/ldap": "^4.4|^5.0", + "symfony/translation": "^4.4|^5.0", "symfony/validator": "^4.4|^5.0", "psr/log": "~1.0" }, diff --git a/src/Symfony/Component/Security/Csrf/Tests/CsrfTokenManagerTest.php b/src/Symfony/Component/Security/Csrf/Tests/CsrfTokenManagerTest.php index 34dc1698b9695..1703397274433 100644 --- a/src/Symfony/Component/Security/Csrf/Tests/CsrfTokenManagerTest.php +++ b/src/Symfony/Component/Security/Csrf/Tests/CsrfTokenManagerTest.php @@ -177,28 +177,28 @@ public function getManagerGeneratorAndStorage() { $data = []; - list($generator, $storage) = $this->getGeneratorAndStorage(); + [$generator, $storage] = $this->getGeneratorAndStorage(); $data[] = ['', new CsrfTokenManager($generator, $storage, ''), $storage, $generator]; - list($generator, $storage) = $this->getGeneratorAndStorage(); + [$generator, $storage] = $this->getGeneratorAndStorage(); $data[] = ['https-', new CsrfTokenManager($generator, $storage), $storage, $generator]; - list($generator, $storage) = $this->getGeneratorAndStorage(); + [$generator, $storage] = $this->getGeneratorAndStorage(); $data[] = ['aNamespace-', new CsrfTokenManager($generator, $storage, 'aNamespace-'), $storage, $generator]; $requestStack = new RequestStack(); $requestStack->push(new Request([], [], [], [], [], ['HTTPS' => 'on'])); - list($generator, $storage) = $this->getGeneratorAndStorage(); + [$generator, $storage] = $this->getGeneratorAndStorage(); $data[] = ['https-', new CsrfTokenManager($generator, $storage, $requestStack), $storage, $generator]; - list($generator, $storage) = $this->getGeneratorAndStorage(); + [$generator, $storage] = $this->getGeneratorAndStorage(); $data[] = ['generated-', new CsrfTokenManager($generator, $storage, function () { return 'generated-'; }), $storage, $generator]; $requestStack = new RequestStack(); $requestStack->push(new Request()); - list($generator, $storage) = $this->getGeneratorAndStorage(); + [$generator, $storage] = $this->getGeneratorAndStorage(); $data[] = ['', new CsrfTokenManager($generator, $storage, $requestStack), $storage, $generator]; return $data; diff --git a/src/Symfony/Component/Security/Csrf/Tests/TokenGenerator/UriSafeTokenGeneratorTest.php b/src/Symfony/Component/Security/Csrf/Tests/TokenGenerator/UriSafeTokenGeneratorTest.php index dc97acc239ddc..ad545aa784bcc 100644 --- a/src/Symfony/Component/Security/Csrf/Tests/TokenGenerator/UriSafeTokenGeneratorTest.php +++ b/src/Symfony/Component/Security/Csrf/Tests/TokenGenerator/UriSafeTokenGeneratorTest.php @@ -19,7 +19,7 @@ */ class UriSafeTokenGeneratorTest extends TestCase { - const ENTROPY = 1000; + private const ENTROPY = 1000; /** * A non alpha-numeric byte string. diff --git a/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/NativeSessionTokenStorageTest.php b/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/NativeSessionTokenStorageTest.php index ead39789979b1..5acbedc6e9335 100644 --- a/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/NativeSessionTokenStorageTest.php +++ b/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/NativeSessionTokenStorageTest.php @@ -22,7 +22,7 @@ */ class NativeSessionTokenStorageTest extends TestCase { - const SESSION_NAMESPACE = 'foobar'; + private const SESSION_NAMESPACE = 'foobar'; /** * @var NativeSessionTokenStorage diff --git a/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php b/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php index 7ddd965e51d7b..af5b0ebc8b779 100644 --- a/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php +++ b/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php @@ -21,7 +21,7 @@ */ class SessionTokenStorageTest extends TestCase { - const SESSION_NAMESPACE = 'foobar'; + private const SESSION_NAMESPACE = 'foobar'; /** * @var Session diff --git a/src/Symfony/Component/Security/Guard/Tests/Authenticator/FormLoginAuthenticatorTest.php b/src/Symfony/Component/Security/Guard/Tests/Authenticator/FormLoginAuthenticatorTest.php index 3d1fc8a4bcfb3..1e6d9d89952c4 100644 --- a/src/Symfony/Component/Security/Guard/Tests/Authenticator/FormLoginAuthenticatorTest.php +++ b/src/Symfony/Component/Security/Guard/Tests/Authenticator/FormLoginAuthenticatorTest.php @@ -29,9 +29,8 @@ class FormLoginAuthenticatorTest extends TestCase private $requestWithSession; private $authenticator; - const LOGIN_URL = 'http://login'; - const DEFAULT_SUCCESS_URL = 'http://defaultsuccess'; - const CUSTOM_SUCCESS_URL = 'http://customsuccess'; + private const LOGIN_URL = 'http://login'; + private const DEFAULT_SUCCESS_URL = 'http://defaultsuccess'; public function testAuthenticationFailureWithoutSession() { diff --git a/src/Symfony/Component/Security/Http/Firewall/ChannelListener.php b/src/Symfony/Component/Security/Http/Firewall/ChannelListener.php index 243faecbc083b..9c89d4376d0f9 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ChannelListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ChannelListener.php @@ -43,7 +43,7 @@ public function __construct(AccessMapInterface $map, AuthenticationEntryPointInt */ public function supports(Request $request): ?bool { - list(, $channel) = $this->map->getPatterns($request); + [, $channel] = $this->map->getPatterns($request); if ('https' === $channel && !$request->isSecure()) { if (null !== $this->logger) { diff --git a/src/Symfony/Component/Security/Http/Logout/LogoutUrlGenerator.php b/src/Symfony/Component/Security/Http/Logout/LogoutUrlGenerator.php index c6b721209f363..478b36448d587 100644 --- a/src/Symfony/Component/Security/Http/Logout/LogoutUrlGenerator.php +++ b/src/Symfony/Component/Security/Http/Logout/LogoutUrlGenerator.php @@ -84,7 +84,7 @@ public function setCurrentFirewall(?string $key, string $context = null) */ private function generateLogoutUrl(?string $key, int $referenceType): string { - list($logoutPath, $csrfTokenId, $csrfParameter, $csrfTokenManager) = $this->getListener($key); + [$logoutPath, $csrfTokenId, $csrfParameter, $csrfTokenManager] = $this->getListener($key); if (null === $logoutPath) { throw new \LogicException('Unable to generate the logout URL without a path.'); @@ -146,7 +146,7 @@ private function getListener(?string $key): array } // Fetch from injected current firewall information, if possible - list($key, $context) = $this->currentFirewall; + [$key, $context] = $this->currentFirewall; if (isset($this->listeners[$key])) { return $this->listeners[$key]; diff --git a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php index bd51ca4cae651..5711f0f5acca8 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php @@ -52,7 +52,7 @@ protected function cancelCookie(Request $request) if (null !== ($cookie = $request->cookies->get($this->options['name'])) && 2 === \count($parts = $this->decodeCookie($cookie)) ) { - list($series) = $parts; + [$series] = $parts; $this->tokenProvider->deleteTokenBySeries($series); } } @@ -66,7 +66,7 @@ protected function processAutoLoginCookie(array $cookieParts, Request $request) throw new AuthenticationException('The cookie is invalid.'); } - list($series, $tokenValue) = $cookieParts; + [$series, $tokenValue] = $cookieParts; $persistentToken = $this->tokenProvider->loadTokenBySeries($series); if (!$this->isTokenValueValid($persistentToken, $tokenValue)) { diff --git a/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php index 38d3a4cce2af9..80e0b058157e2 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php @@ -35,7 +35,7 @@ protected function processAutoLoginCookie(array $cookieParts, Request $request) throw new AuthenticationException('The cookie is invalid.'); } - list($class, $username, $expires, $hash) = $cookieParts; + [$class, $username, $expires, $hash] = $cookieParts; if (false === $username = base64_decode($username, true)) { throw new AuthenticationException('$username contains a character from outside the base64 alphabet.'); } diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/LogoutListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/LogoutListenerTest.php index 76a975d0baeaf..109c841af76d3 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/LogoutListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/LogoutListenerTest.php @@ -28,9 +28,9 @@ class LogoutListenerTest extends TestCase public function testHandleUnmatchedPath() { $dispatcher = $this->getEventDispatcher(); - list($listener, , $httpUtils, $options) = $this->getListener($dispatcher); + [$listener, , $httpUtils, $options] = $this->getListener($dispatcher); - list($event, $request) = $this->getGetResponseEvent(); + [$event, $request] = $this->getGetResponseEvent(); $logoutEventDispatched = false; $dispatcher->addListener(LogoutEvent::class, function (LogoutEvent $event) use (&$logoutEventDispatched) { @@ -52,9 +52,9 @@ public function testHandleMatchedPathWithCsrfValidation() $tokenManager = $this->getTokenManager(); $dispatcher = $this->getEventDispatcher(); - list($listener, $tokenStorage, $httpUtils, $options) = $this->getListener($dispatcher, $tokenManager); + [$listener, $tokenStorage, $httpUtils, $options] = $this->getListener($dispatcher, $tokenManager); - list($event, $request) = $this->getGetResponseEvent(); + [$event, $request] = $this->getGetResponseEvent(); $request->query->set('_csrf_token', 'token'); @@ -90,9 +90,9 @@ public function testHandleMatchedPathWithCsrfValidation() public function testHandleMatchedPathWithoutCsrfValidation() { $dispatcher = $this->getEventDispatcher(); - list($listener, $tokenStorage, $httpUtils, $options) = $this->getListener($dispatcher); + [$listener, $tokenStorage, $httpUtils, $options] = $this->getListener($dispatcher); - list($event, $request) = $this->getGetResponseEvent(); + [$event, $request] = $this->getGetResponseEvent(); $httpUtils->expects($this->once()) ->method('checkRequestPath') @@ -123,9 +123,9 @@ public function testNoResponseSet() { $this->expectException('RuntimeException'); - list($listener, , $httpUtils, $options) = $this->getListener(); + [$listener, , $httpUtils, $options] = $this->getListener(); - list($event, $request) = $this->getGetResponseEvent(); + [$event, $request] = $this->getGetResponseEvent(); $httpUtils->expects($this->once()) ->method('checkRequestPath') @@ -140,9 +140,9 @@ public function testCsrfValidationFails() $this->expectException('Symfony\Component\Security\Core\Exception\LogoutException'); $tokenManager = $this->getTokenManager(); - list($listener, , $httpUtils, $options) = $this->getListener(null, $tokenManager); + [$listener, , $httpUtils, $options] = $this->getListener(null, $tokenManager); - list($event, $request) = $this->getGetResponseEvent(); + [$event, $request] = $this->getGetResponseEvent(); $request->query->set('_csrf_token', 'token'); @@ -168,12 +168,12 @@ public function testLegacyLogoutHandlers() $this->expectDeprecation('Since symfony/security-http 5.1: Calling "%s::addHandler" is deprecated, register a listener on the "%s" event instead.'); $logoutSuccessHandler = $this->createMock(LogoutSuccessHandlerInterface::class); - list($listener, $tokenStorage, $httpUtils, $options) = $this->getListener($logoutSuccessHandler); + [$listener, $tokenStorage, $httpUtils, $options] = $this->getListener($logoutSuccessHandler); $token = $this->getToken(); $tokenStorage->expects($this->any())->method('getToken')->willReturn($token); - list($event, $request) = $this->getGetResponseEvent(); + [$event, $request] = $this->getGetResponseEvent(); $httpUtils->expects($this->once()) ->method('checkRequestPath') diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/RememberMeListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/RememberMeListenerTest.php index d321ed68921bd..2dccec5b9acff 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/RememberMeListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/RememberMeListenerTest.php @@ -25,7 +25,7 @@ class RememberMeListenerTest extends TestCase { public function testOnCoreSecurityDoesNotTryToPopulateNonEmptyTokenStorage() { - list($listener, $tokenStorage) = $this->getListener(); + [$listener, $tokenStorage] = $this->getListener(); $tokenStorage ->expects($this->any()) @@ -43,7 +43,7 @@ public function testOnCoreSecurityDoesNotTryToPopulateNonEmptyTokenStorage() public function testOnCoreSecurityDoesNothingWhenNoCookieIsSet() { - list($listener, $tokenStorage, $service) = $this->getListener(); + [$listener, $tokenStorage, $service] = $this->getListener(); $tokenStorage ->expects($this->any()) @@ -64,7 +64,7 @@ public function testOnCoreSecurityDoesNothingWhenNoCookieIsSet() public function testOnCoreSecurityIgnoresAuthenticationExceptionThrownByAuthenticationManagerImplementation() { - list($listener, $tokenStorage, $service, $manager) = $this->getListener(); + [$listener, $tokenStorage, $service, $manager] = $this->getListener(); $request = new Request(); $exception = new AuthenticationException('Authentication failed.'); @@ -101,7 +101,7 @@ public function testOnCoreSecurityIgnoresAuthenticationOptionallyRethrowsExcepti { $this->expectException('Symfony\Component\Security\Core\Exception\AuthenticationException'); $this->expectExceptionMessage('Authentication failed.'); - list($listener, $tokenStorage, $service, $manager) = $this->getListener(false, false); + [$listener, $tokenStorage, $service, $manager] = $this->getListener(false, false); $tokenStorage ->expects($this->any()) @@ -134,7 +134,7 @@ public function testOnCoreSecurityIgnoresAuthenticationOptionallyRethrowsExcepti public function testOnCoreSecurityAuthenticationExceptionDuringAutoLoginTriggersLoginFail() { - list($listener, $tokenStorage, $service, $manager) = $this->getListener(); + [$listener, $tokenStorage, $service, $manager] = $this->getListener(); $tokenStorage ->expects($this->any()) @@ -166,7 +166,7 @@ public function testOnCoreSecurityAuthenticationExceptionDuringAutoLoginTriggers public function testOnCoreSecurity() { - list($listener, $tokenStorage, $service, $manager) = $this->getListener(); + [$listener, $tokenStorage, $service, $manager] = $this->getListener(); $tokenStorage ->expects($this->any()) @@ -200,7 +200,7 @@ public function testOnCoreSecurity() public function testSessionStrategy() { - list($listener, $tokenStorage, $service, $manager, , , $sessionStrategy) = $this->getListener(false, true, true); + [$listener, $tokenStorage, $service, $manager, , , $sessionStrategy] = $this->getListener(false, true, true); $tokenStorage ->expects($this->any()) @@ -250,7 +250,7 @@ public function testSessionStrategy() public function testSessionIsMigratedByDefault() { - list($listener, $tokenStorage, $service, $manager) = $this->getListener(false, true, false); + [$listener, $tokenStorage, $service, $manager] = $this->getListener(false, true, false); $tokenStorage ->expects($this->any()) @@ -298,7 +298,7 @@ public function testSessionIsMigratedByDefault() public function testOnCoreSecurityInteractiveLoginEventIsDispatchedIfDispatcherIsPresent() { - list($listener, $tokenStorage, $service, $manager, , $dispatcher) = $this->getListener(true); + [$listener, $tokenStorage, $service, $manager, , $dispatcher] = $this->getListener(true); $tokenStorage ->expects($this->any()) diff --git a/src/Symfony/Component/Security/Http/Tests/FirewallMapTest.php b/src/Symfony/Component/Security/Http/Tests/FirewallMapTest.php index c464a4da3ccaf..50675a6003425 100644 --- a/src/Symfony/Component/Security/Http/Tests/FirewallMapTest.php +++ b/src/Symfony/Component/Security/Http/Tests/FirewallMapTest.php @@ -53,7 +53,7 @@ public function testGetListeners() $map->add($tooLateMatcher, [function () {}]); - list($listeners, $exception) = $map->getListeners($request); + [$listeners, $exception] = $map->getListeners($request); $this->assertEquals([$theListener], $listeners); $this->assertEquals($theException, $exception); @@ -88,7 +88,7 @@ public function testGetListenersWithAnEntryHavingNoRequestMatcher() $map->add($tooLateMatcher, [function () {}]); - list($listeners, $exception) = $map->getListeners($request); + [$listeners, $exception] = $map->getListeners($request); $this->assertEquals([$theListener], $listeners); $this->assertEquals($theException, $exception); @@ -110,7 +110,7 @@ public function testGetListenersWithNoMatchingEntry() $map->add($notMatchingMatcher, [function () {}]); - list($listeners, $exception) = $map->getListeners($request); + [$listeners, $exception] = $map->getListeners($request); $this->assertEquals([], $listeners); $this->assertNull($exception); diff --git a/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php b/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php index 523bbdaa47d6b..631defab32764 100644 --- a/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php +++ b/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php @@ -81,7 +81,7 @@ public function encode($data, string $format, array $context = []) } } - list($delimiter, $enclosure, $escapeChar, $keySeparator, $headers, $escapeFormulas, $outputBom) = $this->getCsvOptions($context); + [$delimiter, $enclosure, $escapeChar, $keySeparator, $headers, $escapeFormulas, $outputBom] = $this->getCsvOptions($context); foreach ($data as &$value) { $flattened = []; @@ -142,7 +142,7 @@ public function decode(string $data, string $format, array $context = []) $headerCount = []; $result = []; - list($delimiter, $enclosure, $escapeChar, $keySeparator, , , , $asCollection) = $this->getCsvOptions($context); + [$delimiter, $enclosure, $escapeChar, $keySeparator, , , , $asCollection] = $this->getCsvOptions($context); while (false !== ($cols = fgetcsv($handle, 0, $delimiter, $enclosure, $escapeChar))) { $nbCols = \count($cols); diff --git a/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php index 33892277389e5..854400b538e0d 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php @@ -35,6 +35,8 @@ class ArrayDenormalizer implements ContextAwareDenormalizerInterface, Serializer * {@inheritdoc} * * @throws NotNormalizableValueException + * + * @return array */ public function denormalize($data, string $type, string $format = null, array $context = []) { diff --git a/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php index 4fcee91bda1c3..6be2d44c31a10 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php @@ -41,6 +41,8 @@ public function __construct($defaultContext = [], NameConverterInterface $nameCo /** * {@inheritdoc} + * + * @return array */ public function normalize($object, string $format = null, array $context = []) { diff --git a/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php index 979288cf6ace3..a2f57b57e4a61 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php @@ -47,6 +47,8 @@ public function __construct(MimeTypeGuesserInterface $mimeTypeGuesser = null) /** * {@inheritdoc} + * + * @return string */ public function normalize($object, string $format = null, array $context = []) { @@ -88,6 +90,8 @@ public function supportsNormalization($data, string $format = null) * * @throws InvalidArgumentException * @throws NotNormalizableValueException + * + * @return \SplFileInfo */ public function denormalize($data, string $type, string $format = null, array $context = []) { diff --git a/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php index de028b34341f9..8bfb4c1675428 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php @@ -37,6 +37,8 @@ public function __construct(array $defaultContext = []) * {@inheritdoc} * * @throws InvalidArgumentException + * + * @return string */ public function normalize($object, string $format = null, array $context = []) { @@ -68,6 +70,8 @@ public function hasCacheableSupportsMethod(): bool * * @throws InvalidArgumentException * @throws UnexpectedValueException + * + * @return \DateInterval */ public function denormalize($data, string $type, string $format = null, array $context = []) { diff --git a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php index c12e23470a445..05aad160e3b2c 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php @@ -45,6 +45,8 @@ public function __construct(array $defaultContext = []) * {@inheritdoc} * * @throws InvalidArgumentException + * + * @return string */ public function normalize($object, string $format = null, array $context = []) { @@ -75,6 +77,8 @@ public function supportsNormalization($data, string $format = null) * {@inheritdoc} * * @throws NotNormalizableValueException + * + * @return \DateTimeInterface */ public function denormalize($data, string $type, string $format = null, array $context = []) { diff --git a/src/Symfony/Component/Serializer/Normalizer/DateTimeZoneNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateTimeZoneNormalizer.php index 2ff3f18200fb5..af262ebaad70e 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DateTimeZoneNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DateTimeZoneNormalizer.php @@ -25,6 +25,8 @@ class DateTimeZoneNormalizer implements NormalizerInterface, DenormalizerInterfa * {@inheritdoc} * * @throws InvalidArgumentException + * + * @return string */ public function normalize($object, string $format = null, array $context = []) { @@ -47,6 +49,8 @@ public function supportsNormalization($data, string $format = null) * {@inheritdoc} * * @throws NotNormalizableValueException + * + * @return \DateTimeZone */ public function denormalize($data, string $type, string $format = null, array $context = []) { diff --git a/src/Symfony/Component/Serializer/Normalizer/ProblemNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ProblemNormalizer.php index 51821de2a06d8..a39181fcf37c7 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ProblemNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ProblemNormalizer.php @@ -37,6 +37,8 @@ public function __construct(bool $debug = false, array $defaultContext = []) /** * {@inheritdoc} + * + * @return array */ public function normalize($exception, string $format = null, array $context = []) { diff --git a/src/Symfony/Component/Serializer/Serializer.php b/src/Symfony/Component/Serializer/Serializer.php index b8c33a2fe56c5..6414caf900472 100644 --- a/src/Symfony/Component/Serializer/Serializer.php +++ b/src/Symfony/Component/Serializer/Serializer.php @@ -189,7 +189,10 @@ public function normalize($data, string $format = null, array $context = []) */ public function denormalize($data, string $type, string $format = null, array $context = []) { - if (isset(self::SCALAR_TYPES[$type])) { + $normalizer = $this->getDenormalizer($data, $type, $format, $context); + + // Check for a denormalizer first, e.g. the data is wrapped + if (!$normalizer && isset(self::SCALAR_TYPES[$type])) { if (!('is_'.$type)($data)) { throw new NotNormalizableValueException(sprintf('Data expected to be of type "%s" ("%s" given).', $type, get_debug_type($data))); } @@ -201,7 +204,7 @@ public function denormalize($data, string $type, string $format = null, array $c throw new LogicException('You must register at least one normalizer to be able to denormalize objects.'); } - if ($normalizer = $this->getDenormalizer($data, $type, $format, $context)) { + if ($normalizer) { return $normalizer->denormalize($data, $type, $format, $context); } diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/ChainDecoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/ChainDecoderTest.php index 301b43586970a..215e73386176a 100644 --- a/src/Symfony/Component/Serializer/Tests/Encoder/ChainDecoderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Encoder/ChainDecoderTest.php @@ -16,9 +16,9 @@ class ChainDecoderTest extends TestCase { - const FORMAT_1 = 'format1'; - const FORMAT_2 = 'format2'; - const FORMAT_3 = 'format3'; + private const FORMAT_1 = 'format1'; + private const FORMAT_2 = 'format2'; + private const FORMAT_3 = 'format3'; private $chainDecoder; private $decoder1; diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/ChainEncoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/ChainEncoderTest.php index f2e41ad28d4b6..e1f029b7157e9 100644 --- a/src/Symfony/Component/Serializer/Tests/Encoder/ChainEncoderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Encoder/ChainEncoderTest.php @@ -18,9 +18,9 @@ class ChainEncoderTest extends TestCase { - const FORMAT_1 = 'format1'; - const FORMAT_2 = 'format2'; - const FORMAT_3 = 'format3'; + private const FORMAT_1 = 'format1'; + private const FORMAT_2 = 'format2'; + private const FORMAT_3 = 'format3'; private $chainEncoder; private $encoder1; diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/DataUriNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/DataUriNormalizerTest.php index d1829f759ddac..f1a906b593836 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/DataUriNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/DataUriNormalizerTest.php @@ -20,9 +20,9 @@ */ class DataUriNormalizerTest extends TestCase { - const TEST_GIF_DATA = 'data:image/gif;base64,R0lGODdhAQABAIAAAP///////ywAAAAAAQABAAACAkQBADs='; - const TEST_TXT_DATA = 'data:text/plain,K%C3%A9vin%20Dunglas%0A'; - const TEST_TXT_CONTENT = "Kévin Dunglas\n"; + private const TEST_GIF_DATA = 'data:image/gif;base64,R0lGODdhAQABAIAAAP///////ywAAAAAAQABAAACAkQBADs='; + private const TEST_TXT_DATA = 'data:text/plain,K%C3%A9vin%20Dunglas%0A'; + private const TEST_TXT_CONTENT = "Kévin Dunglas\n"; /** * @var DataUriNormalizer diff --git a/src/Symfony/Component/Serializer/Tests/SerializerTest.php b/src/Symfony/Component/Serializer/Tests/SerializerTest.php index 80c430a7d4323..b72d9030d2155 100644 --- a/src/Symfony/Component/Serializer/Tests/SerializerTest.php +++ b/src/Symfony/Component/Serializer/Tests/SerializerTest.php @@ -613,6 +613,13 @@ public function testDeserializeInconsistentScalarArray() $serializer->deserialize('["42"]', 'int[]', 'json'); } + public function testDeserializeWrappedScalar() + { + $serializer = new Serializer([new UnwrappingDenormalizer()], ['json' => new JsonEncoder()]); + + $this->assertSame(42, $serializer->deserialize('{"wrapper": 42}', 'int', 'json', [UnwrappingDenormalizer::UNWRAP_PATH => '[wrapper]'])); + } + private function serializerWithClassDiscriminator() { $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); diff --git a/src/Symfony/Component/Stopwatch/Tests/StopwatchEventTest.php b/src/Symfony/Component/Stopwatch/Tests/StopwatchEventTest.php index 1647d7bf8d47f..06808616e3307 100644 --- a/src/Symfony/Component/Stopwatch/Tests/StopwatchEventTest.php +++ b/src/Symfony/Component/Stopwatch/Tests/StopwatchEventTest.php @@ -23,7 +23,7 @@ */ class StopwatchEventTest extends TestCase { - const DELTA = 37; + private const DELTA = 37; public function testGetOrigin() { diff --git a/src/Symfony/Component/Stopwatch/Tests/StopwatchTest.php b/src/Symfony/Component/Stopwatch/Tests/StopwatchTest.php index b8efa373b4cd1..5dd0c3c2d8a19 100644 --- a/src/Symfony/Component/Stopwatch/Tests/StopwatchTest.php +++ b/src/Symfony/Component/Stopwatch/Tests/StopwatchTest.php @@ -23,7 +23,7 @@ */ class StopwatchTest extends TestCase { - const DELTA = 20; + private const DELTA = 20; public function testStart() { diff --git a/src/Symfony/Component/Translation/DependencyInjection/TranslatorPathsPass.php b/src/Symfony/Component/Translation/DependencyInjection/TranslatorPathsPass.php index 04975b171f563..80071eee74a74 100644 --- a/src/Symfony/Component/Translation/DependencyInjection/TranslatorPathsPass.php +++ b/src/Symfony/Component/Translation/DependencyInjection/TranslatorPathsPass.php @@ -48,7 +48,7 @@ public function process(ContainerBuilder $container) foreach ($this->findControllerArguments($container) as $controller => $argument) { $id = substr($controller, 0, strpos($controller, ':') ?: \strlen($controller)); if ($container->hasDefinition($id)) { - list($locatorRef) = $argument->getValues(); + [$locatorRef] = $argument->getValues(); $this->controllers[(string) $locatorRef][$container->getDefinition($id)->getClass()] = true; } } diff --git a/src/Symfony/Component/Translation/Loader/MoFileLoader.php b/src/Symfony/Component/Translation/Loader/MoFileLoader.php index d344c6e21af1c..1d90136bdb40c 100644 --- a/src/Symfony/Component/Translation/Loader/MoFileLoader.php +++ b/src/Symfony/Component/Translation/Loader/MoFileLoader.php @@ -90,7 +90,7 @@ protected function loadResource($resource) $singularId = fread($stream, $length); if (false !== strpos($singularId, "\000")) { - list($singularId, $pluralId) = explode("\000", $singularId); + [$singularId, $pluralId] = explode("\000", $singularId); } fseek($stream, $offsetTranslated + $i * 8); diff --git a/src/Symfony/Component/Translation/Util/XliffUtils.php b/src/Symfony/Component/Translation/Util/XliffUtils.php index a8c05c2244d47..e4373a7d5ba1b 100644 --- a/src/Symfony/Component/Translation/Util/XliffUtils.php +++ b/src/Symfony/Component/Translation/Util/XliffUtils.php @@ -61,21 +61,18 @@ public static function validateSchema(\DOMDocument $dom): array { $xliffVersion = static::getVersionNumber($dom); $internalErrors = libxml_use_internal_errors(true); - if (\LIBXML_VERSION < 20900) { + if ($shouldEnable = self::shouldEnableEntityLoader()) { $disableEntities = libxml_disable_entity_loader(false); } - - $isValid = @$dom->schemaValidateSource(self::getSchema($xliffVersion)); - if (!$isValid) { - if (\LIBXML_VERSION < 20900) { + try { + $isValid = @$dom->schemaValidateSource(self::getSchema($xliffVersion)); + if (!$isValid) { + return self::getXmlErrors($internalErrors); + } + } finally { + if ($shouldEnable) { libxml_disable_entity_loader($disableEntities); } - - return self::getXmlErrors($internalErrors); - } - - if (\LIBXML_VERSION < 20900) { - libxml_disable_entity_loader($disableEntities); } $dom->normalizeDocument(); @@ -86,6 +83,36 @@ public static function validateSchema(\DOMDocument $dom): array return []; } + private static function shouldEnableEntityLoader(): bool + { + // Version prior to 8.0 can be enabled without deprecation + if (\PHP_VERSION_ID < 80000) { + return true; + } + + static $dom, $schema; + if (null === $dom) { + $dom = new \DOMDocument(); + $dom->loadXML(''); + + $tmpfile = tempnam(sys_get_temp_dir(), 'symfony'); + register_shutdown_function(static function () use ($tmpfile) { + @unlink($tmpfile); + }); + $schema = ' + + +'; + file_put_contents($tmpfile, ' + + + +'); + } + + return !@$dom->schemaValidateSource($schema); + } + public static function getErrorsAsString(array $xmlErrors): string { $errorsAsString = ''; diff --git a/src/Symfony/Component/Validator/Constraints/FileValidator.php b/src/Symfony/Component/Validator/Constraints/FileValidator.php index 3c946ff96ce8b..e5a756d893c8f 100644 --- a/src/Symfony/Component/Validator/Constraints/FileValidator.php +++ b/src/Symfony/Component/Validator/Constraints/FileValidator.php @@ -63,7 +63,7 @@ public function validate($value, Constraint $constraint) $binaryFormat = null === $constraint->binaryFormat ? true : $constraint->binaryFormat; } - list(, $limitAsString, $suffix) = $this->factorizeSizes(0, $limitInBytes, $binaryFormat); + [, $limitAsString, $suffix] = $this->factorizeSizes(0, $limitInBytes, $binaryFormat); $this->context->buildViolation($constraint->uploadIniSizeErrorMessage) ->setParameter('{{ limit }}', $limitAsString) ->setParameter('{{ suffix }}', $suffix) @@ -157,7 +157,7 @@ public function validate($value, Constraint $constraint) $limitInBytes = $constraint->maxSize; if ($sizeInBytes > $limitInBytes) { - list($sizeAsString, $limitAsString, $suffix) = $this->factorizeSizes($sizeInBytes, $limitInBytes, $constraint->binaryFormat); + [$sizeAsString, $limitAsString, $suffix] = $this->factorizeSizes($sizeInBytes, $limitInBytes, $constraint->binaryFormat); $this->context->buildViolation($constraint->maxSizeMessage) ->setParameter('{{ file }}', $this->formatValue($path)) ->setParameter('{{ size }}', $sizeAsString) diff --git a/src/Symfony/Component/Validator/Constraints/NotCompromisedPasswordValidator.php b/src/Symfony/Component/Validator/Constraints/NotCompromisedPasswordValidator.php index 7b8dd423532ba..adcdb7a59ff54 100644 --- a/src/Symfony/Component/Validator/Constraints/NotCompromisedPasswordValidator.php +++ b/src/Symfony/Component/Validator/Constraints/NotCompromisedPasswordValidator.php @@ -91,7 +91,7 @@ public function validate($value, Constraint $constraint) } foreach (explode("\r\n", $result) as $line) { - list($hashSuffix, $count) = explode(':', $line); + [$hashSuffix, $count] = explode(':', $line); if ($hashPrefix.$hashSuffix === $hash && $constraint->threshold <= (int) $count) { $this->context->buildViolation($constraint->message) diff --git a/src/Symfony/Component/Validator/Context/ExecutionContext.php b/src/Symfony/Component/Validator/Context/ExecutionContext.php index 74ed391034f15..4560793eb5da8 100644 --- a/src/Symfony/Component/Validator/Context/ExecutionContext.php +++ b/src/Symfony/Component/Validator/Context/ExecutionContext.php @@ -128,6 +128,7 @@ class ExecutionContext implements ExecutionContextInterface * @var array */ private $initializedObjects; + private $cachedObjectsRefs; /** * @param mixed $root The root value of the validated object graph @@ -141,6 +142,7 @@ public function __construct(ValidatorInterface $validator, $root, TranslatorInte $this->translator = $translator; $this->translationDomain = $translationDomain; $this->violations = new ConstraintViolationList(); + $this->cachedObjectsRefs = new \SplObjectStorage(); } /** @@ -346,4 +348,20 @@ public function isObjectInitialized(string $cacheKey): bool { return isset($this->initializedObjects[$cacheKey]); } + + /** + * @internal + * + * @param object $object + * + * @return string + */ + public function generateCacheKey($object) + { + if (!isset($this->cachedObjectsRefs[$object])) { + $this->cachedObjectsRefs[$object] = spl_object_hash($object); + } + + return $this->cachedObjectsRefs[$object]; + } } diff --git a/src/Symfony/Component/Validator/Mapping/Loader/AbstractLoader.php b/src/Symfony/Component/Validator/Mapping/Loader/AbstractLoader.php index 1055f41e381b3..deb356eb64d30 100644 --- a/src/Symfony/Component/Validator/Mapping/Loader/AbstractLoader.php +++ b/src/Symfony/Component/Validator/Mapping/Loader/AbstractLoader.php @@ -69,7 +69,7 @@ protected function newConstraint(string $name, $options = null) if (false !== strpos($name, '\\') && class_exists($name)) { $className = (string) $name; } elseif (false !== strpos($name, ':')) { - list($prefix, $className) = explode(':', $name, 2); + [$prefix, $className] = explode(':', $name, 2); if (!isset($this->namespaces[$prefix])) { throw new MappingException(sprintf('Undefined namespace prefix "%s".', $prefix)); diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.af.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.af.xlf index 731e64aa7f2a4..cbee3bb9d31b8 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.af.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.af.xlf @@ -334,6 +334,58 @@ This value should be valid JSON. Hierdie waarde moet geldige JSON wees. + + This collection should contain only unique elements. + Hierdie versameling moet net unieke elemente bevat. + + + This value should be positive. + Hierdie waarde moet positief wees. + + + This value should be either positive or zero. + Hierdie waarde moet positief of nul wees. + + + This value should be negative. + Hierdie waarde moet negatief wees. + + + This value should be either negative or zero. + Hierdie waarde moet negatief of nul wees. + + + This value is not a valid timezone. + Hierdie waarde is nie 'n geldige tydsone nie. + + + This password has been leaked in a data breach, it must not be used. Please use another password. + This password has been leaked in a data breach, it must not be used. Please use another password. + + + This value should be between {{ min }} and {{ max }}. + Hierdie waarde moet tussen {{ min }} en {{ max }} wees. + + + This value is not a valid hostname. + Hierdie waarde is nie 'n geldige gasheernaam nie. + + + The number of elements in this collection should be a multiple of {{ compared_value }}. + Die hoeveelheid elemente in hierdie versameling moet 'n meelvoud van {{ compared_value }} wees. + + + This value should satisfy at least one of the following constraints: + Hierdie waarde moet voldoen aan ten minste een van hierdie beperkings: + + + Each element of this collection should satisfy its own set of constraints. + Elke element van hierdie versameling moet voldoen aan hulle eie beperkings. + + + This value is not a valid International Securities Identification Number (ISIN). + Hierdie waarde is nie 'n geldige Internasionale veiligheidsidentifikasienommer (ISIN) nie. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.az.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.az.xlf index c3420f3db2a94..59480874fd387 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.az.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.az.xlf @@ -366,6 +366,26 @@ This value should be between {{ min }} and {{ max }}. Bu dəyər {{ min }} və {{ max }} arasında olmaldır. + + This value is not a valid hostname. + Bu dəyər doğru bir host adı deyil. + + + The number of elements in this collection should be a multiple of {{ compared_value }}. + Bu kolleksiyadakı elementlerin sayı {{ compared_value }} tam bölünəni olmalıdır. + + + This value should satisfy at least one of the following constraints: + Bu dəyər aşağıdakı məcburiyyətlərdən birini qarşılamalıdır: + + + Each element of this collection should satisfy its own set of constraints. + Bu kolleksiyadakı hər element öz məcburiyyətlərini qarşılamalıdır. + + + This value is not a valid International Securities Identification Number (ISIN). + Bu dəyər doğru bir Qiymətli Kağızın Beynəlxalq İdentifikasiya Kodu (ISIN) deyil. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.ca.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.ca.xlf index d6ae6e91bce2a..80e5364c1d756 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.ca.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.ca.xlf @@ -330,6 +330,62 @@ This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}. Aquest Codi d'identificació bancari (BIC) no està associat amb l'IBAN {{ iban }}. + + This value should be valid JSON. + Aquest valor hauria de ser JSON vàlid. + + + This collection should contain only unique elements. + Aquesta col·lecció només hauria de contenir elements únics. + + + This value should be positive. + Aquest valor hauria de ser positiu. + + + This value should be either positive or zero. + Aquest valor ha de ser positiu o zero. + + + This value should be negative. + Aquest valor ha de ser negatiu. + + + This value should be either negative or zero. + Aquest valor ha de ser negatiu o zero. + + + This value is not a valid timezone. + Aquest valor no és una zona horària vàlida. + + + This password has been leaked in a data breach, it must not be used. Please use another password. + Aquesta contrasenya s'ha filtrat en cas de violació de dades, no s'ha d'utilitzar. Utilitzeu una altra contrasenya. + + + This value should be between {{ min }} and {{ max }}. + Aquest valor ha d'estar entre {{ min }} i {{ max }}. + + + This value is not a valid hostname. + Aquest valor no és un nom d'amfitrió vàlid. + + + The number of elements in this collection should be a multiple of {{ compared_value }}. + El nombre d'elements d'aquesta col·lecció ha de ser múltiple de {{compared_value}}. + + + This value should satisfy at least one of the following constraints: + Aquest valor ha de satisfer almenys una de les restriccions següents: + + + Each element of this collection should satisfy its own set of constraints. + Cada element d'aquesta col·lecció hauria de satisfer el seu propi conjunt de restriccions. + + + This value is not a valid International Securities Identification Number (ISIN). + Aquest valor no és un número d'identificació de valors internacionals (ISIN) vàlid. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.da.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.da.xlf index 2bc33a7b437cd..6716585e7c9e3 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.da.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.da.xlf @@ -124,7 +124,7 @@ The file could not be uploaded. - Filen kunne ikke blive uploadet. + Filen kunne ikke uploades. This value should be a valid number. @@ -324,7 +324,7 @@ This value should be a multiple of {{ compared_value }}. - Denne værdi skal være et flertal af {{ compared_value }}. + Denne værdi skal være et multiplikation af {{ compared_value }}. This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}. @@ -366,6 +366,26 @@ This value should be between {{ min }} and {{ max }}. Værdien skal være mellem {{ min }} og {{ max }}. + + This value is not a valid hostname. + Værdien er ikke et gyldigt værtsnavn. + + + The number of elements in this collection should be a multiple of {{ compared_value }}. + Antallet af elementer i denne samling skal være en multiplikation af {{ compared_value }}. + + + This value should satisfy at least one of the following constraints: + Værdien skal overholde mindst én af følgende krav:: + + + Each element of this collection should satisfy its own set of constraints. + Hvert element i denne samling skal overholde dens egne krav. + + + This value is not a valid International Securities Identification Number (ISIN). + Værdien er ikke et gyldig International Securities Identification Number (ISIN). + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.el.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.el.xlf index 3b9bbef66e5a4..c8882d2ae6667 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.el.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.el.xlf @@ -24,11 +24,11 @@ You must select at least {{ limit }} choice.|You must select at least {{ limit }} choices. - Πρέπει να επιλέξετε τουλάχιστον {{ limit }} επιλογή.|Πρέπει να επιλέξετε τουλάχιστον {{ limit }} επιλογές. + Πρέπει να επιλέξτε τουλάχιστον {{ limit }} επιλογή.|Πρέπει να επιλέξτε τουλάχιστον {{ limit }} επιλογές. You must select at most {{ limit }} choice.|You must select at most {{ limit }} choices. - Πρέπει να επιλέξετε το πολύ {{ limit }} επιλογή.|Πρέπει να επιλέξετε το πολύ {{ limit }} επιλογές. + Πρέπει να επιλέξτε το πολύ {{ limit }} επιλογή.|Πρέπει να επιλέξτε το πολύ {{ limit }} επιλογές. One or more of the given values is invalid. @@ -330,6 +330,62 @@ 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. + Αυτός ο κωδικός πρόσβασης έχει διερρεύσει σε παραβίαση δεδομένων. Παρακαλούμε να χρησιμοποιήσετε έναν άλλο κωδικό. + + + This value should be between {{ min }} and {{ max }}. + Αυτή η τιμή θα πρέπει να είναι μεταξύ {{ min }} και {{ max }}. + + + This value is not a valid hostname. + Αυτή η τιμή δέν είναι έγκυρο όνομα υποδοχής. + + + The number of elements in this collection should be a multiple of {{ compared_value }}. + Το νούμερο των στοιχείων σε αυτή τη συλλογή θα πρέπει να είναι πολλαπλάσιο του {{ compared_value }}. + + + This value should satisfy at least one of the following constraints: + Αυτή η τιμή θα πρέπει να ικανοποιεί τουλάχιστον έναν απο τους παρακάτω περιορισμούς: + + + Each element of this collection should satisfy its own set of constraints. + Κάθε στοιχείο σε αυτή τη συλλογή θα πρέπει να ικανοποιεί το δικό του σύνολο περιορισμών. + + + This value is not a valid International Securities Identification Number (ISIN). + Αυτή η τιμή δέν είναι έγυρο International Securities Identification Number (ISIN). + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.fi.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.fi.xlf index d35bff2c6e8e7..423775903d6c4 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.fi.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.fi.xlf @@ -336,7 +336,7 @@ This collection should contain only unique elements. - Tämä ryhmä tulisi sisältää vain yksilöllisiä arvoja. + Tämän ryhmän tulisi sisältää vain yksilöllisiä arvoja. This value should be positive. @@ -366,6 +366,26 @@ This value should be between {{ min }} and {{ max }}. Arvon tulisi olla välillä {{ min }} - {{ max }}. + + This value is not a valid hostname. + Arvo ei ole kelvollinen laitenimi (hostname). + + + The number of elements in this collection should be a multiple of {{ compared_value }}. + Ryhmässä olevien elementtien määrän pitää olla monikerta luvulle {{ compared_value }}. + + + This value should satisfy at least one of the following constraints: + Tämän arvon tulee läpäistä vähintään yksi seuraavista tarkistuksista: + + + Each element of this collection should satisfy its own set of constraints. + Ryhmän jokaisen elementin tulee läpäistä omat tarkistuksensa. + + + This value is not a valid International Securities Identification Number (ISIN). + Tämä arvo ei ole kelvollinen ISIN-koodi (International Securities Identification Number). + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.gl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.gl.xlf index 71df1d240bde7..4bfd7a09c23f5 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.gl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.gl.xlf @@ -330,6 +330,62 @@ This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}. Este Código de identificación bancaria (BIC) non está asociado co IBAN {{ iban }}. + + This value should be valid JSON. + Este valor debería ser un JSON válido. + + + This collection should contain only unique elements. + Esta colección só debería ter elementos únicos. + + + This value should be positive. + Este valor debería ser positivo. + + + This value should be either positive or zero. + Este valor debe ser positivo ou igual a cero. + + + This value should be negative. + Este valor debe ser negativo. + + + This value should be either negative or zero. + Este valor debe ser negativo ou igual a cero. + + + This value is not a valid timezone. + Este valor non é unha zona horaria válida. + + + This password has been leaked in a data breach, it must not be used. Please use another password. + Este contrasinal non se pode usar porque está incluído nunha lista de contrasinais públicos obtidos grazas a fallos de seguridade noutros sitios e aplicacións. Utiliza outro contrasinal. + + + This value should be between {{ min }} and {{ max }}. + Este valor debe estar comprendido entre {{min}} e {{max}}. + + + This value is not a valid hostname. + Este valor non é un nome de host válido. + + + The number of elements in this collection should be a multiple of {{ compared_value }}. + O número de elementos desta colección debería ser múltiplo de {{compare_value}}. + + + This value should satisfy at least one of the following constraints: + Este valor debe cumprir polo menos unha das seguintes restricións: + + + Each element of this collection should satisfy its own set of constraints. + Cada elemento desta colección debe satisfacer o seu propio conxunto de restricións. + + + This value is not a valid International Securities Identification Number (ISIN). + Este valor non é un número de identificación de valores internacionais (ISIN) válido. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.he.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.he.xlf index 6f9ab0a1cfa64..dfaed73d2f6da 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.he.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.he.xlf @@ -366,6 +366,26 @@ This value should be between {{ min }} and {{ max }}. הערך חייב להיות בין {{ min }} ו- {{ max }}. + + This value is not a valid hostname. + ערך זה אינו שם מארח חוקי. + + + The number of elements in this collection should be a multiple of {{ compared_value }}. + מספר האלמנטים באוסף זה צריך להיות מכפיל של {{ compared_value }}. + + + This value should satisfy at least one of the following constraints: + ערך זה אמור לעמוד לפחות באחד התנאים הבאים: + + + Each element of this collection should satisfy its own set of constraints. + כל אלמנט באוסף זה אמור לעמוד בקבוצת התנאים שלו. + + + This value is not a valid International Securities Identification Number (ISIN). + ערך זה אינו מספר זיהוי ניירות ערך בינלאומי תקף (ISIN). + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.hu.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.hu.xlf index 23ca5618097c5..acd69a1009c13 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.hu.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.hu.xlf @@ -382,6 +382,10 @@ Each element of this collection should satisfy its own set of constraints. A gyűjtemény minden elemének meg kell felelni a saját feltételeinek. + + This value is not a valid International Securities Identification Number (ISIN). + Ez az érték nem egy érvényes nemzetközi értékpapír-azonosító szám (ISIN). + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.mn.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.mn.xlf index b1458eee1ac0b..b767dc87c8452 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.mn.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.mn.xlf @@ -131,20 +131,20 @@ Энэ утга зөвхөн тоо байна. - This value is not a valid country. - Энэ утга үнэн бодит улс биш байна. - - 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. @@ -366,6 +366,26 @@ This value should be between {{ min }} and {{ max }}. Энэ утга {{ min }} -с {{ max }} хооронд байх ёстой. + + This value is not a valid hostname. + Энэ утга буруу hostname байна. + + + The number of elements in this collection should be a multiple of {{ compared_value }}. + Энэхүү цуглуулган дахь элемэнтийн тоо, {{ compared_value }}-н үржвэр байх ёстой. + + + This value should satisfy at least one of the following constraints: + Энэ утга доорх болзолуудын ядаж нэгийг хангах ёстой: + + + Each element of this collection should satisfy its own set of constraints. + Энэхүү цуглуулган дахь элемэнтүүд өөр өөрсдийн болзолуудаа хангах ёстой. + + + This value is not a valid International Securities Identification Number (ISIN). + Энэ утга зөв International Securities Identification Number (ISIN) биш байна. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.nb.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.nb.xlf index bfa9b1284e8d9..93132ec57cdfc 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.nb.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.nb.xlf @@ -366,6 +366,26 @@ This value should be between {{ min }} and {{ max }}. Verdien må være mellom {{ min }} og {{ max }}. + + This value is not a valid hostname. + Denne verdien er ikke et gyldig vertsnavn. + + + The number of elements in this collection should be a multiple of {{ compared_value }}. + Antall elementer i denne samlingen bør være et multiplum av {{ compared_value }}. + + + This value should satisfy at least one of the following constraints: + Denne verdien skal tilfredsstille minst en av følgende begrensninger: + + + Each element of this collection should satisfy its own set of constraints. + Hvert element i denne samlingen skal tilfredsstille sitt eget sett med begrensninger. + + + This value is not a valid International Securities Identification Number (ISIN). + Denne verdien er ikke et gyldig International Securities Identification Number (ISIN). + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.no.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.no.xlf index bfa9b1284e8d9..93132ec57cdfc 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.no.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.no.xlf @@ -366,6 +366,26 @@ This value should be between {{ min }} and {{ max }}. Verdien må være mellom {{ min }} og {{ max }}. + + This value is not a valid hostname. + Denne verdien er ikke et gyldig vertsnavn. + + + The number of elements in this collection should be a multiple of {{ compared_value }}. + Antall elementer i denne samlingen bør være et multiplum av {{ compared_value }}. + + + This value should satisfy at least one of the following constraints: + Denne verdien skal tilfredsstille minst en av følgende begrensninger: + + + Each element of this collection should satisfy its own set of constraints. + Hvert element i denne samlingen skal tilfredsstille sitt eget sett med begrensninger. + + + This value is not a valid International Securities Identification Number (ISIN). + Denne verdien er ikke et gyldig International Securities Identification Number (ISIN). + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.pt.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.pt.xlf index e5cf660686358..0244ee4f398ba 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.pt.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.pt.xlf @@ -128,7 +128,7 @@ This value should be a valid number. - Este valor deveria de ser um número válido. + Este valor deveria ser um número válido. This file is not a valid image. @@ -176,27 +176,27 @@ This value should be the user's current password. - Este valor deveria de ser a password atual do utilizador. + Este valor deveria ser a senha atual do usuário. This value should have exactly {{ limit }} character.|This value should have exactly {{ limit }} characters. - Este valor tem de ter exatamente {{ limit }} carateres. + Este valor deve possuir exatamente {{ limit }} caracteres. The file was only partially uploaded. - Só foi enviado parte do ficheiro. + Só foi enviada uma parte do arquivo. No file was uploaded. - Nenhum ficheiro foi enviado. + Nenhum arquivo foi enviado. No temporary folder was configured in php.ini. - Não existe nenhum directório temporária configurado no ficheiro php.ini. + Não existe uma pasta temporária configurada no arquivo php.ini. Cannot write temporary file to disk. - Não foi possível escrever ficheiros temporários no disco. + Não foi possível escrever os arquivos temporários no disco. A PHP extension caused the upload to fail. @@ -292,15 +292,15 @@ The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed. - A imagem está orientada à paisagem ({{ width }}x{{ height }}px). Imagens orientadas à paisagem não são permitidas. + A imagem está em orientação de paisagem ({{ width }}x{{ height }}px). Imagens orientadas em paisagem não são permitidas. The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed. - A imagem está orientada ao retrato ({{ width }}x{{ height }}px). Imagens orientadas ao retrato não são permitidas. + A imagem está em orientação de retrato ({{ width }}x{{ height }}px). Imagens orientadas em retrato não são permitidas. An empty file is not allowed. - Ficheiro vazio não é permitido. + Um arquivo vazio não é permitido. The host could not be resolved. diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.th.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.th.xlf index 31aa00cfac011..31453ff15736d 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.th.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.th.xlf @@ -366,6 +366,26 @@ This value should be between {{ min }} and {{ max }}. ค่านี้ควรอยู่ระหว่าง {{ min }} ถึง {{ max }} + + This value is not a valid hostname. + ค่าโฮสต์เนมไม่ถูกต้อง + + + The number of elements in this collection should be a multiple of {{ compared_value }}. + จำนวนของสมาชิกในคอเล็กชั่นควรเป็นพหุคูณของ {{ compared_value }} + + + This value should satisfy at least one of the following constraints: + ค่านี้ควรเป็นไปตามข้อจำกัดอย่างน้อยหนึ่งข้อจากข้อจำกัดเหล่านี้: + + + Each element of this collection should satisfy its own set of constraints. + สมากชิกแต่ละตัวในคอเล็กชั่นควรเป็นไปตามเซ็ตข้อจำกัดของคอเล็กชั่น + + + This value is not a valid International Securities Identification Number (ISIN). + ค่า​รหัสหลักทรัพย์สากล (ISIN) ไม่ถูกต้อง + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.tl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.tl.xlf index 34754b1e9bb8d..e930e59014191 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.tl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.tl.xlf @@ -68,7 +68,7 @@ The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }}. - Ang uri ng file ng mime ay hindi balido ({{ type }}).Ang mga pinapayagang uri ng mime ay ang {{ types }}. + Ang uri ng file ng mime ay hindi balido ({{ type }}). Ang mga pinapayagang uri ng mime ay ang {{ types }}. This value should be {{ limit }} or less. @@ -76,7 +76,7 @@ This value is too long. It should have {{ limit }} character or less.|This value is too long. It should have {{ limit }} characters or less. - Ang halaga nito ay masyadong mahaba.Ito ay dapat na {{ limit }} karakter o maliit pa.|Ang halaga nito ay masyadong mahaba. Ito ay dapat na {{ limit }} mga karakter o maliit pa. + Ang halaga nito ay masyadong mahaba. Ito ay dapat na {{ limit }} karakter o maliit pa.|Ang halaga nito ay masyadong mahaba. Ito ay dapat na {{ limit }} mga karakter o maliit pa. This value should be {{ limit }} or more. @@ -268,7 +268,7 @@ This value should be less than or equal to {{ compared_value }}. - Ang halagang ito ay dapat mas mmaliit o magkapareha sa {{ compared_value }}. + Ang halagang ito ay dapat mas maliit o magkapareha sa {{ compared_value }}. This value should not be equal to {{ compared_value }}. @@ -284,7 +284,7 @@ The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}. - ng ratio ng imahe ay masyadong maliit ({{ ratio }}). Ang pinamaliit na ratio ay {{ min_ratio }}. + Ang ratio ng imahe ay masyadong maliit ({{ ratio }}). Ang pinakamaliit na ratio ay {{ min_ratio }}. The image is square ({{ width }}x{{ height }}px). Square images are not allowed. @@ -296,7 +296,7 @@ The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed. - Ang orientasyon ng imahe ay nakaportrait ({{ width }}x{{ height }}px). PAng mga imaheng nakaportrait ang orientasyon ay hindi pwede. + Ang orientasyon ng imahe ay nakaportrait ({{ width }}x{{ height }}px). Ang mga imaheng nakaportrait ang orientasyon ay hindi pwede. An empty file is not allowed. @@ -374,6 +374,14 @@ The number of elements in this collection should be a multiple of {{ compared_value }}. Ang bilang ng mga elemento sa koleksyon na ito ay dapat multiple ng {{ compared_value }}. + + This value should satisfy at least one of the following constraints: + Ang halagang ito ay dapat masunod ang kahit na isang sumusunod na batayan. + + + Each element of this collection should satisfy its own set of constraints. + Ang bawat elemento sa koleksyon na ito ay dapat masunod ang nararapat na batayan. + This value is not a valid International Securities Identification Number (ISIN). Ang halagang ito ay hindi wastong International Securities Identification Number (ISIN). diff --git a/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php b/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php index cce35db0dd0b9..7a5518c699152 100644 --- a/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php +++ b/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php @@ -473,7 +473,7 @@ public function doValidate($value, $constraints = null, $groups = null) return $this; } - list($expectedValue, $expectedGroup, $expectedConstraints, $violation) = $this->expectedValidate[$this->validateCalls]; + [$expectedValue, $expectedGroup, $expectedConstraints, $violation] = $this->expectedValidate[$this->validateCalls]; Assert::assertSame($expectedValue, $value); $expectedConstraints($constraints); diff --git a/src/Symfony/Component/Validator/Tests/Constraints/AbstractComparisonValidatorTestCase.php b/src/Symfony/Component/Validator/Tests/Constraints/AbstractComparisonValidatorTestCase.php index efc79b0b78c97..38d7076cf2882 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/AbstractComparisonValidatorTestCase.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/AbstractComparisonValidatorTestCase.php @@ -202,7 +202,7 @@ public function testInvalidComparisonToValue($dirtyValue, $dirtyValueAsString, $ public function testInvalidComparisonToPropertyPathAddsPathAsParameter() { - list($dirtyValue, $dirtyValueAsString, $comparedValue, $comparedValueString, $comparedValueType) = current($this->provideAllInvalidComparisons()); + [$dirtyValue, $dirtyValueAsString, $comparedValue, $comparedValueString, $comparedValueType] = current($this->provideAllInvalidComparisons()); $constraint = $this->createConstraint(['propertyPath' => 'value']); $constraint->message = 'Constraint Message'; diff --git a/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php index ee0798f611dc6..a61e212e01c90 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php @@ -456,7 +456,7 @@ public function uploadedFileErrorProvider() $reflection = new \ReflectionClass(\get_class(new FileValidator())); $method = $reflection->getMethod('factorizeSizes'); $method->setAccessible(true); - list(, $limit, $suffix) = $method->invokeArgs(new FileValidator(), [0, UploadedFile::getMaxFilesize(), false]); + [, $limit, $suffix] = $method->invokeArgs(new FileValidator(), [0, UploadedFile::getMaxFilesize(), false]); // it correctly parses the maxSize option and not only uses simple string comparison // 1000M should be bigger than the ini value diff --git a/src/Symfony/Component/Validator/Tests/Mapping/ClassMetadataTest.php b/src/Symfony/Component/Validator/Tests/Mapping/ClassMetadataTest.php index f0635266e29c5..93834fbf447f4 100644 --- a/src/Symfony/Component/Validator/Tests/Mapping/ClassMetadataTest.php +++ b/src/Symfony/Component/Validator/Tests/Mapping/ClassMetadataTest.php @@ -24,10 +24,10 @@ class ClassMetadataTest extends TestCase { - const CLASSNAME = 'Symfony\Component\Validator\Tests\Fixtures\Entity'; - const PARENTCLASS = 'Symfony\Component\Validator\Tests\Fixtures\EntityParent'; - const PROVIDERCLASS = 'Symfony\Component\Validator\Tests\Fixtures\GroupSequenceProviderEntity'; - const PROVIDERCHILDCLASS = 'Symfony\Component\Validator\Tests\Fixtures\GroupSequenceProviderChildEntity'; + private const CLASSNAME = 'Symfony\Component\Validator\Tests\Fixtures\Entity'; + private const PARENTCLASS = 'Symfony\Component\Validator\Tests\Fixtures\EntityParent'; + private const PROVIDERCLASS = 'Symfony\Component\Validator\Tests\Fixtures\GroupSequenceProviderEntity'; + private const PROVIDERCHILDCLASS = 'Symfony\Component\Validator\Tests\Fixtures\GroupSequenceProviderChildEntity'; protected $metadata; diff --git a/src/Symfony/Component/Validator/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php b/src/Symfony/Component/Validator/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php index 14709fb8571fc..e379943e52967 100644 --- a/src/Symfony/Component/Validator/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php +++ b/src/Symfony/Component/Validator/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php @@ -25,11 +25,9 @@ class LazyLoadingMetadataFactoryTest extends TestCase { - const CLASS_NAME = 'Symfony\Component\Validator\Tests\Fixtures\Entity'; - const PARENT_CLASS = 'Symfony\Component\Validator\Tests\Fixtures\EntityParent'; - const INTERFACE_A_CLASS = 'Symfony\Component\Validator\Tests\Fixtures\EntityInterfaceA'; - const INTERFACE_B_CLASS = 'Symfony\Component\Validator\Tests\Fixtures\EntityInterfaceB'; - const PARENT_INTERFACE_CLASS = 'Symfony\Component\Validator\Tests\Fixtures\EntityParentInterface'; + private const CLASS_NAME = 'Symfony\Component\Validator\Tests\Fixtures\Entity'; + private const PARENT_CLASS = 'Symfony\Component\Validator\Tests\Fixtures\EntityParent'; + private const INTERFACE_A_CLASS = 'Symfony\Component\Validator\Tests\Fixtures\EntityInterfaceA'; public function testLoadClassMetadataWithInterface() { diff --git a/src/Symfony/Component/Validator/Tests/Mapping/GetterMetadataTest.php b/src/Symfony/Component/Validator/Tests/Mapping/GetterMetadataTest.php index 127bd5a164b47..442afaf134331 100644 --- a/src/Symfony/Component/Validator/Tests/Mapping/GetterMetadataTest.php +++ b/src/Symfony/Component/Validator/Tests/Mapping/GetterMetadataTest.php @@ -17,7 +17,7 @@ class GetterMetadataTest extends TestCase { - const CLASSNAME = 'Symfony\Component\Validator\Tests\Fixtures\Entity'; + private const CLASSNAME = 'Symfony\Component\Validator\Tests\Fixtures\Entity'; public function testInvalidPropertyName() { diff --git a/src/Symfony/Component/Validator/Tests/Mapping/PropertyMetadataTest.php b/src/Symfony/Component/Validator/Tests/Mapping/PropertyMetadataTest.php index 8868ec64aac9f..3796677294553 100644 --- a/src/Symfony/Component/Validator/Tests/Mapping/PropertyMetadataTest.php +++ b/src/Symfony/Component/Validator/Tests/Mapping/PropertyMetadataTest.php @@ -19,10 +19,10 @@ class PropertyMetadataTest extends TestCase { - const CLASSNAME = 'Symfony\Component\Validator\Tests\Fixtures\Entity'; - const CLASSNAME_74 = 'Symfony\Component\Validator\Tests\Fixtures\Entity_74'; - const CLASSNAME_74_PROXY = 'Symfony\Component\Validator\Tests\Fixtures\Entity_74_Proxy'; - const PARENTCLASS = 'Symfony\Component\Validator\Tests\Fixtures\EntityParent'; + private const CLASSNAME = 'Symfony\Component\Validator\Tests\Fixtures\Entity'; + private const CLASSNAME_74 = 'Symfony\Component\Validator\Tests\Fixtures\Entity_74'; + private const CLASSNAME_74_PROXY = 'Symfony\Component\Validator\Tests\Fixtures\Entity_74_Proxy'; + private const PARENTCLASS = 'Symfony\Component\Validator\Tests\Fixtures\EntityParent'; public function testInvalidPropertyName() { diff --git a/src/Symfony/Component/Validator/Tests/Resources/TranslationFilesTest.php b/src/Symfony/Component/Validator/Tests/Resources/TranslationFilesTest.php index 9779f43727222..6e0620b517563 100644 --- a/src/Symfony/Component/Validator/Tests/Resources/TranslationFilesTest.php +++ b/src/Symfony/Component/Validator/Tests/Resources/TranslationFilesTest.php @@ -12,7 +12,7 @@ namespace Symfony\Component\Validator\Tests\Resources; use PHPUnit\Framework\TestCase; -use PHPUnit\Util\Xml\Loader; +use Symfony\Component\Translation\Util\XliffUtils; class TranslationFilesTest extends TestCase { @@ -21,13 +21,26 @@ class TranslationFilesTest extends TestCase */ public function testTranslationFileIsValid($filePath) { - $loader = class_exists(Loader::class) - ? [new Loader(), 'loadFile'] - : ['PHPUnit\Util\XML', 'loadfile']; + $document = new \DOMDocument(); + $document->loadXML(file_get_contents($filePath)); - $loader($filePath, false, false, true); + $errors = XliffUtils::validateSchema($document); - $this->addToAssertionCount(1); + $this->assertCount(0, $errors, sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message')))); + } + + /** + * @dataProvider provideTranslationFiles + */ + public function testTranslationFileIsValidWithoutEntityLoader($filePath) + { + $document = new \DOMDocument(); + $document->loadXML(file_get_contents($filePath)); + libxml_disable_entity_loader(true); + + $errors = XliffUtils::validateSchema($document); + + $this->assertCount(0, $errors, sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message')))); } public function provideTranslationFiles() diff --git a/src/Symfony/Component/Validator/Tests/Validator/AbstractTest.php b/src/Symfony/Component/Validator/Tests/Validator/AbstractTest.php deleted file mode 100644 index 06f7e85775276..0000000000000 --- a/src/Symfony/Component/Validator/Tests/Validator/AbstractTest.php +++ /dev/null @@ -1,722 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Tests\Validator; - -use Symfony\Component\Validator\Constraints\Callback; -use Symfony\Component\Validator\Constraints\Collection; -use Symfony\Component\Validator\Constraints\Expression; -use Symfony\Component\Validator\Constraints\GroupSequence; -use Symfony\Component\Validator\Constraints\NotBlank; -use Symfony\Component\Validator\Constraints\NotNull; -use Symfony\Component\Validator\Constraints\Traverse; -use Symfony\Component\Validator\Constraints\Valid; -use Symfony\Component\Validator\ConstraintViolationInterface; -use Symfony\Component\Validator\Context\ExecutionContextInterface; -use Symfony\Component\Validator\Mapping\ClassMetadata; -use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface; -use Symfony\Component\Validator\Tests\Fixtures\Entity; -use Symfony\Component\Validator\Tests\Fixtures\FailingConstraint; -use Symfony\Component\Validator\Tests\Fixtures\Reference; -use Symfony\Component\Validator\Validator\ValidatorInterface; - -/** - * @author Bernhard Schussek - */ -abstract class AbstractTest extends AbstractValidatorTest -{ - /** - * @var ValidatorInterface - */ - protected $validator; - - abstract protected function createValidator(MetadataFactoryInterface $metadataFactory, array $objectInitializers = []): ValidatorInterface; - - protected function setUp(): void - { - parent::setUp(); - - $this->validator = $this->createValidator($this->metadataFactory); - } - - protected function validate($value, $constraints = null, $groups = null) - { - return $this->validator->validate($value, $constraints, $groups); - } - - protected function validateProperty($object, $propertyName, $groups = null) - { - return $this->validator->validateProperty($object, $propertyName, $groups); - } - - protected function validatePropertyValue($object, $propertyName, $value, $groups = null) - { - return $this->validator->validatePropertyValue($object, $propertyName, $value, $groups); - } - - public function testValidateConstraintWithoutGroup() - { - $violations = $this->validator->validate(null, new NotNull()); - - $this->assertCount(1, $violations); - } - - public function testValidateWithEmptyArrayAsConstraint() - { - $violations = $this->validator->validate('value', []); - $this->assertCount(0, $violations); - } - - public function testGroupSequenceAbortsAfterFailedGroup() - { - $entity = new Entity(); - - $callback1 = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Message 1'); - }; - $callback2 = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Message 2'); - }; - - $this->metadata->addConstraint(new Callback([ - 'callback' => function () {}, - 'groups' => 'Group 1', - ])); - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback1, - 'groups' => 'Group 2', - ])); - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback2, - 'groups' => 'Group 3', - ])); - - $sequence = new GroupSequence(['Group 1', 'Group 2', 'Group 3']); - $violations = $this->validator->validate($entity, new Valid(), $sequence); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Message 1', $violations[0]->getMessage()); - } - - public function testGroupSequenceIncludesReferences() - { - $entity = new Entity(); - $entity->reference = new Reference(); - - $callback1 = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Reference violation 1'); - }; - $callback2 = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Reference violation 2'); - }; - - $this->metadata->addPropertyConstraint('reference', new Valid()); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback1, - 'groups' => 'Group 1', - ])); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback2, - 'groups' => 'Group 2', - ])); - - $sequence = new GroupSequence(['Group 1', 'Entity']); - $violations = $this->validator->validate($entity, new Valid(), $sequence); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Reference violation 1', $violations[0]->getMessage()); - } - - public function testValidateInSeparateContext() - { - $entity = new Entity(); - $entity->reference = new Reference(); - - $callback1 = function ($value, ExecutionContextInterface $context) use ($entity) { - $violations = $context - ->getValidator() - // Since the validator is not context aware, the group must - // be passed explicitly - ->validate($value->reference, new Valid(), 'Group') - ; - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Message value', $violations[0]->getMessage()); - $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); - $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); - $this->assertSame('', $violations[0]->getPropertyPath()); - - // The root is different as we're in a new context - $this->assertSame($entity->reference, $violations[0]->getRoot()); - $this->assertSame($entity->reference, $violations[0]->getInvalidValue()); - $this->assertNull($violations[0]->getPlural()); - $this->assertNull($violations[0]->getCode()); - - // Verify that this method is called - $context->addViolation('Separate violation'); - }; - - $callback2 = function ($value, ExecutionContextInterface $context) use ($entity) { - $this->assertSame($this::REFERENCE_CLASS, $context->getClassName()); - $this->assertNull($context->getPropertyName()); - $this->assertSame('', $context->getPropertyPath()); - $this->assertSame('Group', $context->getGroup()); - $this->assertSame($this->referenceMetadata, $context->getMetadata()); - $this->assertSame($entity->reference, $context->getRoot()); - $this->assertSame($entity->reference, $context->getValue()); - $this->assertSame($entity->reference, $value); - - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback1, - 'groups' => 'Group', - ])); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback2, - 'groups' => 'Group', - ])); - - $violations = $this->validator->validate($entity, new Valid(), 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Separate violation', $violations[0]->getMessage()); - } - - public function testValidateInContext() - { - $entity = new Entity(); - $entity->reference = new Reference(); - - $callback1 = function ($value, ExecutionContextInterface $context) { - $previousValue = $context->getValue(); - $previousObject = $context->getObject(); - $previousMetadata = $context->getMetadata(); - $previousPath = $context->getPropertyPath(); - $previousGroup = $context->getGroup(); - - $context - ->getValidator() - ->inContext($context) - ->atPath('subpath') - ->validate($value->reference) - ; - - // context changes shouldn't leak out of the validate() call - $this->assertSame($previousValue, $context->getValue()); - $this->assertSame($previousObject, $context->getObject()); - $this->assertSame($previousMetadata, $context->getMetadata()); - $this->assertSame($previousPath, $context->getPropertyPath()); - $this->assertSame($previousGroup, $context->getGroup()); - }; - - $callback2 = function ($value, ExecutionContextInterface $context) use ($entity) { - $this->assertSame($this::REFERENCE_CLASS, $context->getClassName()); - $this->assertNull($context->getPropertyName()); - $this->assertSame('subpath', $context->getPropertyPath()); - $this->assertSame('Group', $context->getGroup()); - $this->assertSame($this->referenceMetadata, $context->getMetadata()); - $this->assertSame($entity, $context->getRoot()); - $this->assertSame($entity->reference, $context->getValue()); - $this->assertSame($entity->reference, $value); - - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback1, - 'groups' => 'Group', - ])); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback2, - 'groups' => 'Group', - ])); - - $violations = $this->validator->validate($entity, new Valid(), 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Message value', $violations[0]->getMessage()); - $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); - $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); - $this->assertSame('subpath', $violations[0]->getPropertyPath()); - $this->assertSame($entity, $violations[0]->getRoot()); - $this->assertSame($entity->reference, $violations[0]->getInvalidValue()); - $this->assertNull($violations[0]->getPlural()); - $this->assertNull($violations[0]->getCode()); - } - - public function testValidateArrayInContext() - { - $entity = new Entity(); - $entity->reference = new Reference(); - - $callback1 = function ($value, ExecutionContextInterface $context) { - $previousValue = $context->getValue(); - $previousObject = $context->getObject(); - $previousMetadata = $context->getMetadata(); - $previousPath = $context->getPropertyPath(); - $previousGroup = $context->getGroup(); - - $context - ->getValidator() - ->inContext($context) - ->atPath('subpath') - ->validate(['key' => $value->reference]) - ; - - // context changes shouldn't leak out of the validate() call - $this->assertSame($previousValue, $context->getValue()); - $this->assertSame($previousObject, $context->getObject()); - $this->assertSame($previousMetadata, $context->getMetadata()); - $this->assertSame($previousPath, $context->getPropertyPath()); - $this->assertSame($previousGroup, $context->getGroup()); - }; - - $callback2 = function ($value, ExecutionContextInterface $context) use ($entity) { - $this->assertSame($this::REFERENCE_CLASS, $context->getClassName()); - $this->assertNull($context->getPropertyName()); - $this->assertSame('subpath[key]', $context->getPropertyPath()); - $this->assertSame('Group', $context->getGroup()); - $this->assertSame($this->referenceMetadata, $context->getMetadata()); - $this->assertSame($entity, $context->getRoot()); - $this->assertSame($entity->reference, $context->getValue()); - $this->assertSame($entity->reference, $value); - - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback1, - 'groups' => 'Group', - ])); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback2, - 'groups' => 'Group', - ])); - - $violations = $this->validator->validate($entity, new Valid(), 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Message value', $violations[0]->getMessage()); - $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); - $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); - $this->assertSame('subpath[key]', $violations[0]->getPropertyPath()); - $this->assertSame($entity, $violations[0]->getRoot()); - $this->assertSame($entity->reference, $violations[0]->getInvalidValue()); - $this->assertNull($violations[0]->getPlural()); - $this->assertNull($violations[0]->getCode()); - } - - public function testTraverseTraversableByDefault() - { - $entity = new Entity(); - $traversable = new \ArrayIterator(['key' => $entity]); - - $callback = function ($value, ExecutionContextInterface $context) use ($entity, $traversable) { - $this->assertSame($this::ENTITY_CLASS, $context->getClassName()); - $this->assertNull($context->getPropertyName()); - $this->assertSame('[key]', $context->getPropertyPath()); - $this->assertSame('Group', $context->getGroup()); - $this->assertSame($this->metadata, $context->getMetadata()); - $this->assertSame($traversable, $context->getRoot()); - $this->assertSame($entity, $context->getValue()); - $this->assertSame($entity, $value); - - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $this->metadataFactory->addMetadata(new ClassMetadata('ArrayIterator')); - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); - - $violations = $this->validate($traversable, new Valid(), 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Message value', $violations[0]->getMessage()); - $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); - $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); - $this->assertSame('[key]', $violations[0]->getPropertyPath()); - $this->assertSame($traversable, $violations[0]->getRoot()); - $this->assertSame($entity, $violations[0]->getInvalidValue()); - $this->assertNull($violations[0]->getPlural()); - $this->assertNull($violations[0]->getCode()); - } - - public function testTraversalEnabledOnClass() - { - $entity = new Entity(); - $traversable = new \ArrayIterator(['key' => $entity]); - - $callback = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Message'); - }; - - $traversableMetadata = new ClassMetadata('ArrayIterator'); - $traversableMetadata->addConstraint(new Traverse(true)); - - $this->metadataFactory->addMetadata($traversableMetadata); - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); - - $violations = $this->validate($traversable, new Valid(), 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - } - - public function testTraversalDisabledOnClass() - { - $entity = new Entity(); - $traversable = new \ArrayIterator(['key' => $entity]); - - $callback = function ($value, ExecutionContextInterface $context) { - $this->fail('Should not be called'); - }; - - $traversableMetadata = new ClassMetadata('ArrayIterator'); - $traversableMetadata->addConstraint(new Traverse(false)); - - $this->metadataFactory->addMetadata($traversableMetadata); - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); - - $violations = $this->validate($traversable, new Valid(), 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(0, $violations); - } - - public function testExpectTraversableIfTraversalEnabledOnClass() - { - $this->expectException('Symfony\Component\Validator\Exception\ConstraintDefinitionException'); - $entity = new Entity(); - - $this->metadata->addConstraint(new Traverse(true)); - - $this->validator->validate($entity); - } - - public function testReferenceTraversalDisabledOnClass() - { - $entity = new Entity(); - $entity->reference = new \ArrayIterator(['key' => new Reference()]); - - $callback = function ($value, ExecutionContextInterface $context) { - $this->fail('Should not be called'); - }; - - $traversableMetadata = new ClassMetadata('ArrayIterator'); - $traversableMetadata->addConstraint(new Traverse(false)); - - $this->metadataFactory->addMetadata($traversableMetadata); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); - $this->metadata->addPropertyConstraint('reference', new Valid()); - - $violations = $this->validate($entity, new Valid(), 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(0, $violations); - } - - public function testReferenceTraversalEnabledOnReferenceDisabledOnClass() - { - $entity = new Entity(); - $entity->reference = new \ArrayIterator(['key' => new Reference()]); - - $callback = function ($value, ExecutionContextInterface $context) { - $this->fail('Should not be called'); - }; - - $traversableMetadata = new ClassMetadata('ArrayIterator'); - $traversableMetadata->addConstraint(new Traverse(false)); - - $this->metadataFactory->addMetadata($traversableMetadata); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); - $this->metadata->addPropertyConstraint('reference', new Valid([ - 'traverse' => true, - ])); - - $violations = $this->validate($entity, new Valid(), 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(0, $violations); - } - - public function testReferenceTraversalDisabledOnReferenceEnabledOnClass() - { - $entity = new Entity(); - $entity->reference = new \ArrayIterator(['key' => new Reference()]); - - $callback = function ($value, ExecutionContextInterface $context) { - $this->fail('Should not be called'); - }; - - $traversableMetadata = new ClassMetadata('ArrayIterator'); - $traversableMetadata->addConstraint(new Traverse(true)); - - $this->metadataFactory->addMetadata($traversableMetadata); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); - $this->metadata->addPropertyConstraint('reference', new Valid([ - 'traverse' => false, - ])); - - $violations = $this->validate($entity, new Valid(), 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(0, $violations); - } - - public function testAddCustomizedViolation() - { - $entity = new Entity(); - - $callback = function ($value, ExecutionContextInterface $context) { - $context->buildViolation('Message %param%') - ->setParameter('%param%', 'value') - ->setInvalidValue('Invalid value') - ->setPlural(2) - ->setCode('42') - ->addViolation(); - }; - - $this->metadata->addConstraint(new Callback($callback)); - - $violations = $this->validator->validate($entity); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Message value', $violations[0]->getMessage()); - $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); - $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); - $this->assertSame('', $violations[0]->getPropertyPath()); - $this->assertSame($entity, $violations[0]->getRoot()); - $this->assertSame('Invalid value', $violations[0]->getInvalidValue()); - $this->assertSame(2, $violations[0]->getPlural()); - $this->assertSame('42', $violations[0]->getCode()); - } - - public function testNoDuplicateValidationIfClassConstraintInMultipleGroups() - { - $entity = new Entity(); - - $callback = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Message'); - }; - - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => ['Group 1', 'Group 2'], - ])); - - $violations = $this->validator->validate($entity, new Valid(), ['Group 1', 'Group 2']); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - } - - public function testNoDuplicateValidationIfPropertyConstraintInMultipleGroups() - { - $entity = new Entity(); - - $callback = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Message'); - }; - - $this->metadata->addPropertyConstraint('firstName', new Callback([ - 'callback' => $callback, - 'groups' => ['Group 1', 'Group 2'], - ])); - - $violations = $this->validator->validate($entity, new Valid(), ['Group 1', 'Group 2']); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - } - - public function testValidateFailsIfNoConstraintsAndNoObjectOrArray() - { - $this->expectException('Symfony\Component\Validator\Exception\RuntimeException'); - $this->validate('Foobar'); - } - - public function testAccessCurrentObject() - { - $called = false; - $entity = new Entity(); - $entity->firstName = 'Bernhard'; - $entity->data = ['firstName' => 'Bernhard']; - - $callback = function ($value, ExecutionContextInterface $context) use ($entity, &$called) { - $called = true; - $this->assertSame($entity, $context->getObject()); - }; - - $this->metadata->addConstraint(new Callback($callback)); - $this->metadata->addPropertyConstraint('firstName', new Callback($callback)); - $this->metadata->addPropertyConstraint('data', new Collection(['firstName' => new Expression('value == this.firstName')])); - - $this->validator->validate($entity); - - $this->assertTrue($called); - } - - public function testInitializeObjectsOnFirstValidation() - { - $entity = new Entity(); - $entity->initialized = false; - - // prepare initializers that set "initialized" to true - $initializer1 = $this->getMockBuilder('Symfony\\Component\\Validator\\ObjectInitializerInterface')->getMock(); - $initializer2 = $this->getMockBuilder('Symfony\\Component\\Validator\\ObjectInitializerInterface')->getMock(); - - $initializer1->expects($this->once()) - ->method('initialize') - ->with($entity) - ->willReturnCallback(function ($object) { - $object->initialized = true; - }); - - $initializer2->expects($this->once()) - ->method('initialize') - ->with($entity); - - $this->validator = $this->createValidator($this->metadataFactory, [ - $initializer1, - $initializer2, - ]); - - // prepare constraint which - // * checks that "initialized" is set to true - // * validates the object again - $callback = function ($object, ExecutionContextInterface $context) { - $this->assertTrue($object->initialized); - - // validate again in same group - $validator = $context->getValidator()->inContext($context); - - $validator->validate($object); - - // validate again in other group - $validator->validate($object, null, 'SomeGroup'); - }; - - $this->metadata->addConstraint(new Callback($callback)); - - $this->validate($entity); - - $this->assertTrue($entity->initialized); - } - - public function testPassConstraintToViolation() - { - $constraint = new FailingConstraint(); - $violations = $this->validate('Foobar', $constraint); - - $this->assertCount(1, $violations); - $this->assertSame($constraint, $violations[0]->getConstraint()); - } - - public function testCollectionConstraitViolationHasCorrectContext() - { - $data = [ - 'foo' => 'fooValue', - ]; - - // Missing field must not be the first in the collection validation - $constraint = new Collection([ - 'foo' => new NotNull(), - 'bar' => new NotNull(), - ]); - - $violations = $this->validate($data, $constraint); - - $this->assertCount(1, $violations); - $this->assertSame($constraint, $violations[0]->getConstraint()); - } - - public function testNestedObjectIsNotValidatedIfGroupInValidConstraintIsNotValidated() - { - $entity = new Entity(); - $entity->firstName = ''; - $reference = new Reference(); - $reference->value = ''; - $entity->childA = $reference; - - $this->metadata->addPropertyConstraint('firstName', new NotBlank(['groups' => 'group1'])); - $this->metadata->addPropertyConstraint('childA', new Valid(['groups' => 'group1'])); - $this->referenceMetadata->addPropertyConstraint('value', new NotBlank()); - - $violations = $this->validator->validate($entity, null, []); - - $this->assertCount(0, $violations); - } - - public function testNestedObjectIsValidatedIfGroupInValidConstraintIsValidated() - { - $entity = new Entity(); - $entity->firstName = ''; - $reference = new Reference(); - $reference->value = ''; - $entity->childA = $reference; - - $this->metadata->addPropertyConstraint('firstName', new NotBlank(['groups' => 'group1'])); - $this->metadata->addPropertyConstraint('childA', new Valid(['groups' => 'group1'])); - $this->referenceMetadata->addPropertyConstraint('value', new NotBlank(['groups' => 'group1'])); - - $violations = $this->validator->validate($entity, null, ['Default', 'group1']); - - $this->assertCount(2, $violations); - } - - public function testNestedObjectIsValidatedInMultipleGroupsIfGroupInValidConstraintIsValidated() - { - $entity = new Entity(); - $entity->firstName = null; - - $reference = new Reference(); - $reference->value = null; - - $entity->childA = $reference; - - $this->metadata->addPropertyConstraint('firstName', new NotBlank()); - $this->metadata->addPropertyConstraint('childA', new Valid(['groups' => ['group1', 'group2']])); - - $this->referenceMetadata->addPropertyConstraint('value', new NotBlank(['groups' => 'group1'])); - $this->referenceMetadata->addPropertyConstraint('value', new NotNull(['groups' => 'group2'])); - - $violations = $this->validator->validate($entity, null, ['Default', 'group1', 'group2']); - - $this->assertCount(3, $violations); - } -} diff --git a/src/Symfony/Component/Validator/Tests/Validator/AbstractValidatorTest.php b/src/Symfony/Component/Validator/Tests/Validator/AbstractValidatorTest.php deleted file mode 100644 index e9bad07096664..0000000000000 --- a/src/Symfony/Component/Validator/Tests/Validator/AbstractValidatorTest.php +++ /dev/null @@ -1,1281 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Tests\Validator; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Validator\Constraints\Callback; -use Symfony\Component\Validator\Constraints\GroupSequence; -use Symfony\Component\Validator\Constraints\Valid; -use Symfony\Component\Validator\ConstraintViolationInterface; -use Symfony\Component\Validator\Context\ExecutionContextInterface; -use Symfony\Component\Validator\Mapping\ClassMetadata; -use Symfony\Component\Validator\Tests\Fixtures\Entity; -use Symfony\Component\Validator\Tests\Fixtures\FakeMetadataFactory; -use Symfony\Component\Validator\Tests\Fixtures\GroupSequenceProviderEntity; -use Symfony\Component\Validator\Tests\Fixtures\Reference; - -/** - * @author Bernhard Schussek - */ -abstract class AbstractValidatorTest extends TestCase -{ - const ENTITY_CLASS = 'Symfony\Component\Validator\Tests\Fixtures\Entity'; - - const REFERENCE_CLASS = 'Symfony\Component\Validator\Tests\Fixtures\Reference'; - - const LAZY_PROPERTY = 'Symfony\Component\Validator\Validator\LazyProperty'; - - /** - * @var FakeMetadataFactory - */ - public $metadataFactory; - - /** - * @var ClassMetadata - */ - public $metadata; - - /** - * @var ClassMetadata - */ - public $referenceMetadata; - - protected function setUp(): void - { - $this->metadataFactory = new FakeMetadataFactory(); - $this->metadata = new ClassMetadata(self::ENTITY_CLASS); - $this->referenceMetadata = new ClassMetadata(self::REFERENCE_CLASS); - $this->metadataFactory->addMetadata($this->metadata); - $this->metadataFactory->addMetadata($this->referenceMetadata); - $this->metadataFactory->addMetadata(new ClassMetadata(self::LAZY_PROPERTY)); - } - - protected function tearDown(): void - { - $this->metadataFactory = null; - $this->metadata = null; - $this->referenceMetadata = null; - } - - abstract protected function validate($value, $constraints = null, $groups = null); - - abstract protected function validateProperty($object, $propertyName, $groups = null); - - abstract protected function validatePropertyValue($object, $propertyName, $value, $groups = null); - - public function testValidate() - { - $callback = function ($value, ExecutionContextInterface $context) { - $this->assertNull($context->getClassName()); - $this->assertNull($context->getPropertyName()); - $this->assertSame('', $context->getPropertyPath()); - $this->assertSame('Group', $context->getGroup()); - $this->assertSame('Bernhard', $context->getRoot()); - $this->assertSame('Bernhard', $context->getValue()); - $this->assertSame('Bernhard', $value); - - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $constraint = new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ]); - - $violations = $this->validate('Bernhard', $constraint, 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Message value', $violations[0]->getMessage()); - $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); - $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); - $this->assertSame('', $violations[0]->getPropertyPath()); - $this->assertSame('Bernhard', $violations[0]->getRoot()); - $this->assertSame('Bernhard', $violations[0]->getInvalidValue()); - $this->assertNull($violations[0]->getPlural()); - $this->assertNull($violations[0]->getCode()); - } - - public function testClassConstraint() - { - $entity = new Entity(); - - $callback = function ($value, ExecutionContextInterface $context) use ($entity) { - $this->assertSame($this::ENTITY_CLASS, $context->getClassName()); - $this->assertNull($context->getPropertyName()); - $this->assertSame('', $context->getPropertyPath()); - $this->assertSame('Group', $context->getGroup()); - $this->assertSame($this->metadata, $context->getMetadata()); - $this->assertSame($entity, $context->getRoot()); - $this->assertSame($entity, $context->getValue()); - $this->assertSame($entity, $value); - - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); - - $violations = $this->validate($entity, null, 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Message value', $violations[0]->getMessage()); - $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); - $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); - $this->assertSame('', $violations[0]->getPropertyPath()); - $this->assertSame($entity, $violations[0]->getRoot()); - $this->assertSame($entity, $violations[0]->getInvalidValue()); - $this->assertNull($violations[0]->getPlural()); - $this->assertNull($violations[0]->getCode()); - } - - public function testPropertyConstraint() - { - $entity = new Entity(); - $entity->firstName = 'Bernhard'; - - $callback = function ($value, ExecutionContextInterface $context) use ($entity) { - $propertyMetadatas = $this->metadata->getPropertyMetadata('firstName'); - - $this->assertSame($this::ENTITY_CLASS, $context->getClassName()); - $this->assertSame('firstName', $context->getPropertyName()); - $this->assertSame('firstName', $context->getPropertyPath()); - $this->assertSame('Group', $context->getGroup()); - $this->assertSame($propertyMetadatas[0], $context->getMetadata()); - $this->assertSame($entity, $context->getRoot()); - $this->assertSame('Bernhard', $context->getValue()); - $this->assertSame('Bernhard', $value); - - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $this->metadata->addPropertyConstraint('firstName', new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); - - $violations = $this->validate($entity, null, 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Message value', $violations[0]->getMessage()); - $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); - $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); - $this->assertSame('firstName', $violations[0]->getPropertyPath()); - $this->assertSame($entity, $violations[0]->getRoot()); - $this->assertSame('Bernhard', $violations[0]->getInvalidValue()); - $this->assertNull($violations[0]->getPlural()); - $this->assertNull($violations[0]->getCode()); - } - - public function testGetterConstraint() - { - $entity = new Entity(); - $entity->setLastName('Schussek'); - - $callback = function ($value, ExecutionContextInterface $context) use ($entity) { - $propertyMetadatas = $this->metadata->getPropertyMetadata('lastName'); - - $this->assertSame($this::ENTITY_CLASS, $context->getClassName()); - $this->assertSame('lastName', $context->getPropertyName()); - $this->assertSame('lastName', $context->getPropertyPath()); - $this->assertSame('Group', $context->getGroup()); - $this->assertSame($propertyMetadatas[0], $context->getMetadata()); - $this->assertSame($entity, $context->getRoot()); - $this->assertSame('Schussek', $context->getValue()); - $this->assertSame('Schussek', $value); - - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $this->metadata->addGetterConstraint('lastName', new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); - - $violations = $this->validate($entity, null, 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Message value', $violations[0]->getMessage()); - $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); - $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); - $this->assertSame('lastName', $violations[0]->getPropertyPath()); - $this->assertSame($entity, $violations[0]->getRoot()); - $this->assertSame('Schussek', $violations[0]->getInvalidValue()); - $this->assertNull($violations[0]->getPlural()); - $this->assertNull($violations[0]->getCode()); - } - - public function testArray() - { - $entity = new Entity(); - $array = ['key' => $entity]; - - $callback = function ($value, ExecutionContextInterface $context) use ($entity, $array) { - $this->assertSame($this::ENTITY_CLASS, $context->getClassName()); - $this->assertNull($context->getPropertyName()); - $this->assertSame('[key]', $context->getPropertyPath()); - $this->assertSame('Group', $context->getGroup()); - $this->assertSame($this->metadata, $context->getMetadata()); - $this->assertSame($array, $context->getRoot()); - $this->assertSame($entity, $context->getValue()); - $this->assertSame($entity, $value); - - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); - - $violations = $this->validate($array, null, 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Message value', $violations[0]->getMessage()); - $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); - $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); - $this->assertSame('[key]', $violations[0]->getPropertyPath()); - $this->assertSame($array, $violations[0]->getRoot()); - $this->assertSame($entity, $violations[0]->getInvalidValue()); - $this->assertNull($violations[0]->getPlural()); - $this->assertNull($violations[0]->getCode()); - } - - public function testRecursiveArray() - { - $entity = new Entity(); - $array = [2 => ['key' => $entity]]; - - $callback = function ($value, ExecutionContextInterface $context) use ($entity, $array) { - $this->assertSame($this::ENTITY_CLASS, $context->getClassName()); - $this->assertNull($context->getPropertyName()); - $this->assertSame('[2][key]', $context->getPropertyPath()); - $this->assertSame('Group', $context->getGroup()); - $this->assertSame($this->metadata, $context->getMetadata()); - $this->assertSame($array, $context->getRoot()); - $this->assertSame($entity, $context->getValue()); - $this->assertSame($entity, $value); - - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); - - $violations = $this->validate($array, null, 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Message value', $violations[0]->getMessage()); - $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); - $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); - $this->assertSame('[2][key]', $violations[0]->getPropertyPath()); - $this->assertSame($array, $violations[0]->getRoot()); - $this->assertSame($entity, $violations[0]->getInvalidValue()); - $this->assertNull($violations[0]->getPlural()); - $this->assertNull($violations[0]->getCode()); - } - - public function testTraversable() - { - $entity = new Entity(); - $traversable = new \ArrayIterator(['key' => $entity]); - - $callback = function ($value, ExecutionContextInterface $context) use ($entity, $traversable) { - $this->assertSame($this::ENTITY_CLASS, $context->getClassName()); - $this->assertNull($context->getPropertyName()); - $this->assertSame('[key]', $context->getPropertyPath()); - $this->assertSame('Group', $context->getGroup()); - $this->assertSame($this->metadata, $context->getMetadata()); - $this->assertSame($traversable, $context->getRoot()); - $this->assertSame($entity, $context->getValue()); - $this->assertSame($entity, $value); - - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); - - $violations = $this->validate($traversable, null, 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Message value', $violations[0]->getMessage()); - $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); - $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); - $this->assertSame('[key]', $violations[0]->getPropertyPath()); - $this->assertSame($traversable, $violations[0]->getRoot()); - $this->assertSame($entity, $violations[0]->getInvalidValue()); - $this->assertNull($violations[0]->getPlural()); - $this->assertNull($violations[0]->getCode()); - } - - public function testRecursiveTraversable() - { - $entity = new Entity(); - $traversable = new \ArrayIterator([ - 2 => new \ArrayIterator(['key' => $entity]), - ]); - - $callback = function ($value, ExecutionContextInterface $context) use ($entity, $traversable) { - $this->assertSame($this::ENTITY_CLASS, $context->getClassName()); - $this->assertNull($context->getPropertyName()); - $this->assertSame('[2][key]', $context->getPropertyPath()); - $this->assertSame('Group', $context->getGroup()); - $this->assertSame($this->metadata, $context->getMetadata()); - $this->assertSame($traversable, $context->getRoot()); - $this->assertSame($entity, $context->getValue()); - $this->assertSame($entity, $value); - - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); - - $violations = $this->validate($traversable, null, 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Message value', $violations[0]->getMessage()); - $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); - $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); - $this->assertSame('[2][key]', $violations[0]->getPropertyPath()); - $this->assertSame($traversable, $violations[0]->getRoot()); - $this->assertSame($entity, $violations[0]->getInvalidValue()); - $this->assertNull($violations[0]->getPlural()); - $this->assertNull($violations[0]->getCode()); - } - - public function testReferenceClassConstraint() - { - $entity = new Entity(); - $entity->reference = new Reference(); - - $callback = function ($value, ExecutionContextInterface $context) use ($entity) { - $this->assertSame($this::REFERENCE_CLASS, $context->getClassName()); - $this->assertNull($context->getPropertyName()); - $this->assertSame('reference', $context->getPropertyPath()); - $this->assertSame('Group', $context->getGroup()); - $this->assertSame($this->referenceMetadata, $context->getMetadata()); - $this->assertSame($entity, $context->getRoot()); - $this->assertSame($entity->reference, $context->getValue()); - $this->assertSame($entity->reference, $value); - - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $this->metadata->addPropertyConstraint('reference', new Valid()); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); - - $violations = $this->validate($entity, null, 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Message value', $violations[0]->getMessage()); - $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); - $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); - $this->assertSame('reference', $violations[0]->getPropertyPath()); - $this->assertSame($entity, $violations[0]->getRoot()); - $this->assertSame($entity->reference, $violations[0]->getInvalidValue()); - $this->assertNull($violations[0]->getPlural()); - $this->assertNull($violations[0]->getCode()); - } - - public function testReferencePropertyConstraint() - { - $entity = new Entity(); - $entity->reference = new Reference(); - $entity->reference->value = 'Foobar'; - - $callback = function ($value, ExecutionContextInterface $context) use ($entity) { - $propertyMetadatas = $this->referenceMetadata->getPropertyMetadata('value'); - - $this->assertSame($this::REFERENCE_CLASS, $context->getClassName()); - $this->assertSame('value', $context->getPropertyName()); - $this->assertSame('reference.value', $context->getPropertyPath()); - $this->assertSame('Group', $context->getGroup()); - $this->assertSame($propertyMetadatas[0], $context->getMetadata()); - $this->assertSame($entity, $context->getRoot()); - $this->assertSame('Foobar', $context->getValue()); - $this->assertSame('Foobar', $value); - - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $this->metadata->addPropertyConstraint('reference', new Valid()); - $this->referenceMetadata->addPropertyConstraint('value', new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); - - $violations = $this->validate($entity, null, 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Message value', $violations[0]->getMessage()); - $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); - $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); - $this->assertSame('reference.value', $violations[0]->getPropertyPath()); - $this->assertSame($entity, $violations[0]->getRoot()); - $this->assertSame('Foobar', $violations[0]->getInvalidValue()); - $this->assertNull($violations[0]->getPlural()); - $this->assertNull($violations[0]->getCode()); - } - - public function testReferenceGetterConstraint() - { - $entity = new Entity(); - $entity->reference = new Reference(); - $entity->reference->setPrivateValue('Bamboo'); - - $callback = function ($value, ExecutionContextInterface $context) use ($entity) { - $propertyMetadatas = $this->referenceMetadata->getPropertyMetadata('privateValue'); - - $this->assertSame($this::REFERENCE_CLASS, $context->getClassName()); - $this->assertSame('privateValue', $context->getPropertyName()); - $this->assertSame('reference.privateValue', $context->getPropertyPath()); - $this->assertSame('Group', $context->getGroup()); - $this->assertSame($propertyMetadatas[0], $context->getMetadata()); - $this->assertSame($entity, $context->getRoot()); - $this->assertSame('Bamboo', $context->getValue()); - $this->assertSame('Bamboo', $value); - - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $this->metadata->addPropertyConstraint('reference', new Valid()); - $this->referenceMetadata->addPropertyConstraint('privateValue', new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); - - $violations = $this->validate($entity, null, 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Message value', $violations[0]->getMessage()); - $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); - $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); - $this->assertSame('reference.privateValue', $violations[0]->getPropertyPath()); - $this->assertSame($entity, $violations[0]->getRoot()); - $this->assertSame('Bamboo', $violations[0]->getInvalidValue()); - $this->assertNull($violations[0]->getPlural()); - $this->assertNull($violations[0]->getCode()); - } - - public function testsIgnoreNullReference() - { - $entity = new Entity(); - $entity->reference = null; - - $this->metadata->addPropertyConstraint('reference', new Valid()); - - $violations = $this->validate($entity); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(0, $violations); - } - - public function testFailOnScalarReferences() - { - $this->expectException('Symfony\Component\Validator\Exception\NoSuchMetadataException'); - $entity = new Entity(); - $entity->reference = 'string'; - - $this->metadata->addPropertyConstraint('reference', new Valid()); - - $this->validate($entity); - } - - /** - * @dataProvider getConstraintMethods - */ - public function testArrayReference($constraintMethod) - { - $entity = new Entity(); - $entity->reference = ['key' => new Reference()]; - - $callback = function ($value, ExecutionContextInterface $context) use ($entity) { - $this->assertSame($this::REFERENCE_CLASS, $context->getClassName()); - $this->assertNull($context->getPropertyName()); - $this->assertSame('reference[key]', $context->getPropertyPath()); - $this->assertSame('Group', $context->getGroup()); - $this->assertSame($this->referenceMetadata, $context->getMetadata()); - $this->assertSame($entity, $context->getRoot()); - $this->assertSame($entity->reference['key'], $context->getValue()); - $this->assertSame($entity->reference['key'], $value); - - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $this->metadata->$constraintMethod('reference', new Valid()); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); - - $violations = $this->validate($entity, null, 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Message value', $violations[0]->getMessage()); - $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); - $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); - $this->assertSame('reference[key]', $violations[0]->getPropertyPath()); - $this->assertSame($entity, $violations[0]->getRoot()); - $this->assertSame($entity->reference['key'], $violations[0]->getInvalidValue()); - $this->assertNull($violations[0]->getPlural()); - $this->assertNull($violations[0]->getCode()); - } - - /** - * @dataProvider getConstraintMethods - */ - public function testRecursiveArrayReference($constraintMethod) - { - $entity = new Entity(); - $entity->reference = [2 => ['key' => new Reference()]]; - - $callback = function ($value, ExecutionContextInterface $context) use ($entity) { - $this->assertSame($this::REFERENCE_CLASS, $context->getClassName()); - $this->assertNull($context->getPropertyName()); - $this->assertSame('reference[2][key]', $context->getPropertyPath()); - $this->assertSame('Group', $context->getGroup()); - $this->assertSame($this->referenceMetadata, $context->getMetadata()); - $this->assertSame($entity, $context->getRoot()); - $this->assertSame($entity->reference[2]['key'], $context->getValue()); - $this->assertSame($entity->reference[2]['key'], $value); - - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $this->metadata->$constraintMethod('reference', new Valid()); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); - - $violations = $this->validate($entity, null, 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Message value', $violations[0]->getMessage()); - $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); - $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); - $this->assertSame('reference[2][key]', $violations[0]->getPropertyPath()); - $this->assertSame($entity, $violations[0]->getRoot()); - $this->assertSame($entity->reference[2]['key'], $violations[0]->getInvalidValue()); - $this->assertNull($violations[0]->getPlural()); - $this->assertNull($violations[0]->getCode()); - } - - public function testOnlyCascadedArraysAreTraversed() - { - $entity = new Entity(); - $entity->reference = ['key' => new Reference()]; - - $callback = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $this->metadata->addPropertyConstraint('reference', new Callback([ - 'callback' => function () {}, - 'groups' => 'Group', - ])); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); - - $violations = $this->validate($entity, null, 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(0, $violations); - } - - /** - * @dataProvider getConstraintMethods - */ - public function testArrayTraversalCannotBeDisabled($constraintMethod) - { - $entity = new Entity(); - $entity->reference = ['key' => new Reference()]; - - $callback = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $this->metadata->$constraintMethod('reference', new Valid([ - 'traverse' => false, - ])); - $this->referenceMetadata->addConstraint(new Callback($callback)); - - $violations = $this->validate($entity); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - } - - /** - * @dataProvider getConstraintMethods - */ - public function testRecursiveArrayTraversalCannotBeDisabled($constraintMethod) - { - $entity = new Entity(); - $entity->reference = [2 => ['key' => new Reference()]]; - - $callback = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $this->metadata->$constraintMethod('reference', new Valid([ - 'traverse' => false, - ])); - - $this->referenceMetadata->addConstraint(new Callback($callback)); - - $violations = $this->validate($entity); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - } - - /** - * @dataProvider getConstraintMethods - */ - public function testIgnoreScalarsDuringArrayTraversal($constraintMethod) - { - $entity = new Entity(); - $entity->reference = ['string', 1234]; - - $this->metadata->$constraintMethod('reference', new Valid()); - - $violations = $this->validate($entity); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(0, $violations); - } - - /** - * @dataProvider getConstraintMethods - */ - public function testIgnoreNullDuringArrayTraversal($constraintMethod) - { - $entity = new Entity(); - $entity->reference = [null]; - - $this->metadata->$constraintMethod('reference', new Valid()); - - $violations = $this->validate($entity); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(0, $violations); - } - - public function testTraversableReference() - { - $entity = new Entity(); - $entity->reference = new \ArrayIterator(['key' => new Reference()]); - - $callback = function ($value, ExecutionContextInterface $context) use ($entity) { - $this->assertSame($this::REFERENCE_CLASS, $context->getClassName()); - $this->assertNull($context->getPropertyName()); - $this->assertSame('reference[key]', $context->getPropertyPath()); - $this->assertSame('Group', $context->getGroup()); - $this->assertSame($this->referenceMetadata, $context->getMetadata()); - $this->assertSame($entity, $context->getRoot()); - $this->assertSame($entity->reference['key'], $context->getValue()); - $this->assertSame($entity->reference['key'], $value); - - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $this->metadata->addPropertyConstraint('reference', new Valid()); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); - - $violations = $this->validate($entity, null, 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Message value', $violations[0]->getMessage()); - $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); - $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); - $this->assertSame('reference[key]', $violations[0]->getPropertyPath()); - $this->assertSame($entity, $violations[0]->getRoot()); - $this->assertSame($entity->reference['key'], $violations[0]->getInvalidValue()); - $this->assertNull($violations[0]->getPlural()); - $this->assertNull($violations[0]->getCode()); - } - - public function testDisableTraversableTraversal() - { - $entity = new Entity(); - $entity->reference = new \ArrayIterator(['key' => new Reference()]); - - $callback = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $this->metadataFactory->addMetadata(new ClassMetadata('ArrayIterator')); - $this->metadata->addPropertyConstraint('reference', new Valid([ - 'traverse' => false, - ])); - $this->referenceMetadata->addConstraint(new Callback($callback)); - - $violations = $this->validate($entity); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(0, $violations); - } - - public function testMetadataMustExistIfTraversalIsDisabled() - { - $this->expectException('Symfony\Component\Validator\Exception\NoSuchMetadataException'); - $entity = new Entity(); - $entity->reference = new \ArrayIterator(); - - $this->metadata->addPropertyConstraint('reference', new Valid([ - 'traverse' => false, - ])); - - $this->validate($entity); - } - - public function testEnableRecursiveTraversableTraversal() - { - $entity = new Entity(); - $entity->reference = new \ArrayIterator([ - 2 => new \ArrayIterator(['key' => new Reference()]), - ]); - - $callback = function ($value, ExecutionContextInterface $context) use ($entity) { - $this->assertSame($this::REFERENCE_CLASS, $context->getClassName()); - $this->assertNull($context->getPropertyName()); - $this->assertSame('reference[2][key]', $context->getPropertyPath()); - $this->assertSame('Group', $context->getGroup()); - $this->assertSame($this->referenceMetadata, $context->getMetadata()); - $this->assertSame($entity, $context->getRoot()); - $this->assertSame($entity->reference[2]['key'], $context->getValue()); - $this->assertSame($entity->reference[2]['key'], $value); - - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $this->metadata->addPropertyConstraint('reference', new Valid([ - 'traverse' => true, - ])); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); - - $violations = $this->validate($entity, null, 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Message value', $violations[0]->getMessage()); - $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); - $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); - $this->assertSame('reference[2][key]', $violations[0]->getPropertyPath()); - $this->assertSame($entity, $violations[0]->getRoot()); - $this->assertSame($entity->reference[2]['key'], $violations[0]->getInvalidValue()); - $this->assertNull($violations[0]->getPlural()); - $this->assertNull($violations[0]->getCode()); - } - - public function testValidateProperty() - { - $entity = new Entity(); - $entity->firstName = 'Bernhard'; - $entity->setLastName('Schussek'); - - $callback1 = function ($value, ExecutionContextInterface $context) use ($entity) { - $propertyMetadatas = $this->metadata->getPropertyMetadata('firstName'); - - $this->assertSame($this::ENTITY_CLASS, $context->getClassName()); - $this->assertSame('firstName', $context->getPropertyName()); - $this->assertSame('firstName', $context->getPropertyPath()); - $this->assertSame('Group', $context->getGroup()); - $this->assertSame($propertyMetadatas[0], $context->getMetadata()); - $this->assertSame($entity, $context->getRoot()); - $this->assertSame('Bernhard', $context->getValue()); - $this->assertSame('Bernhard', $value); - - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $callback2 = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Other violation'); - }; - - $this->metadata->addPropertyConstraint('firstName', new Callback([ - 'callback' => $callback1, - 'groups' => 'Group', - ])); - $this->metadata->addPropertyConstraint('lastName', new Callback([ - 'callback' => $callback2, - 'groups' => 'Group', - ])); - - $violations = $this->validateProperty($entity, 'firstName', 'Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Message value', $violations[0]->getMessage()); - $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); - $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); - $this->assertSame('firstName', $violations[0]->getPropertyPath()); - $this->assertSame($entity, $violations[0]->getRoot()); - $this->assertSame('Bernhard', $violations[0]->getInvalidValue()); - $this->assertNull($violations[0]->getPlural()); - $this->assertNull($violations[0]->getCode()); - } - - /** - * https://github.com/symfony/symfony/issues/11604. - */ - public function testValidatePropertyWithoutConstraints() - { - $entity = new Entity(); - $violations = $this->validateProperty($entity, 'lastName'); - - $this->assertCount(0, $violations, '->validateProperty() returns no violations if no constraints have been configured for the property being validated'); - } - - public function testValidatePropertyValue() - { - $entity = new Entity(); - $entity->setLastName('Schussek'); - - $callback1 = function ($value, ExecutionContextInterface $context) use ($entity) { - $propertyMetadatas = $this->metadata->getPropertyMetadata('firstName'); - - $this->assertSame($this::ENTITY_CLASS, $context->getClassName()); - $this->assertSame('firstName', $context->getPropertyName()); - $this->assertSame('firstName', $context->getPropertyPath()); - $this->assertSame('Group', $context->getGroup()); - $this->assertSame($propertyMetadatas[0], $context->getMetadata()); - $this->assertSame($entity, $context->getRoot()); - $this->assertSame('Bernhard', $context->getValue()); - $this->assertSame('Bernhard', $value); - - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $callback2 = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Other violation'); - }; - - $this->metadata->addPropertyConstraint('firstName', new Callback([ - 'callback' => $callback1, - 'groups' => 'Group', - ])); - $this->metadata->addPropertyConstraint('lastName', new Callback([ - 'callback' => $callback2, - 'groups' => 'Group', - ])); - - $violations = $this->validatePropertyValue( - $entity, - 'firstName', - 'Bernhard', - 'Group' - ); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Message value', $violations[0]->getMessage()); - $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); - $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); - $this->assertSame('firstName', $violations[0]->getPropertyPath()); - $this->assertSame($entity, $violations[0]->getRoot()); - $this->assertSame('Bernhard', $violations[0]->getInvalidValue()); - $this->assertNull($violations[0]->getPlural()); - $this->assertNull($violations[0]->getCode()); - } - - public function testValidatePropertyValueWithClassName() - { - $callback1 = function ($value, ExecutionContextInterface $context) { - $propertyMetadatas = $this->metadata->getPropertyMetadata('firstName'); - - $this->assertSame($this::ENTITY_CLASS, $context->getClassName()); - $this->assertSame('firstName', $context->getPropertyName()); - $this->assertSame('', $context->getPropertyPath()); - $this->assertSame('Group', $context->getGroup()); - $this->assertSame($propertyMetadatas[0], $context->getMetadata()); - $this->assertSame('Bernhard', $context->getRoot()); - $this->assertSame('Bernhard', $context->getValue()); - $this->assertSame('Bernhard', $value); - - $context->addViolation('Message %param%', ['%param%' => 'value']); - }; - - $callback2 = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Other violation'); - }; - - $this->metadata->addPropertyConstraint('firstName', new Callback([ - 'callback' => $callback1, - 'groups' => 'Group', - ])); - $this->metadata->addPropertyConstraint('lastName', new Callback([ - 'callback' => $callback2, - 'groups' => 'Group', - ])); - - $violations = $this->validatePropertyValue( - self::ENTITY_CLASS, - 'firstName', - 'Bernhard', - 'Group' - ); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Message value', $violations[0]->getMessage()); - $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); - $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); - $this->assertSame('', $violations[0]->getPropertyPath()); - $this->assertSame('Bernhard', $violations[0]->getRoot()); - $this->assertSame('Bernhard', $violations[0]->getInvalidValue()); - $this->assertNull($violations[0]->getPlural()); - $this->assertNull($violations[0]->getCode()); - } - - /** - * https://github.com/symfony/symfony/issues/11604. - */ - public function testValidatePropertyValueWithoutConstraints() - { - $entity = new Entity(); - $violations = $this->validatePropertyValue($entity, 'lastName', 'foo'); - - $this->assertCount(0, $violations, '->validatePropertyValue() returns no violations if no constraints have been configured for the property being validated'); - } - - public function testValidateObjectOnlyOncePerGroup() - { - $entity = new Entity(); - $entity->reference = new Reference(); - $entity->reference2 = $entity->reference; - - $callback = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Message'); - }; - - $this->metadata->addPropertyConstraint('reference', new Valid()); - $this->metadata->addPropertyConstraint('reference2', new Valid()); - $this->referenceMetadata->addConstraint(new Callback($callback)); - - $violations = $this->validate($entity); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - } - - public function testValidateDifferentObjectsSeparately() - { - $entity = new Entity(); - $entity->reference = new Reference(); - $entity->reference2 = new Reference(); - - $callback = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Message'); - }; - - $this->metadata->addPropertyConstraint('reference', new Valid()); - $this->metadata->addPropertyConstraint('reference2', new Valid()); - $this->referenceMetadata->addConstraint(new Callback($callback)); - - $violations = $this->validate($entity); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(2, $violations); - } - - public function testValidateSingleGroup() - { - $entity = new Entity(); - - $callback = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Message'); - }; - - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group 1', - ])); - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group 2', - ])); - - $violations = $this->validate($entity, null, 'Group 2'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - } - - public function testValidateMultipleGroups() - { - $entity = new Entity(); - - $callback = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Message'); - }; - - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group 1', - ])); - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group 2', - ])); - - $violations = $this->validate($entity, null, ['Group 1', 'Group 2']); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(2, $violations); - } - - public function testReplaceDefaultGroupByGroupSequenceObject() - { - $entity = new Entity(); - - $callback1 = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Violation in Group 2'); - }; - $callback2 = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Violation in Group 3'); - }; - - $this->metadata->addConstraint(new Callback([ - 'callback' => function () {}, - 'groups' => 'Group 1', - ])); - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback1, - 'groups' => 'Group 2', - ])); - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback2, - 'groups' => 'Group 3', - ])); - - $sequence = new GroupSequence(['Group 1', 'Group 2', 'Group 3', 'Entity']); - $this->metadata->setGroupSequence($sequence); - - $violations = $this->validate($entity, null, 'Default'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Violation in Group 2', $violations[0]->getMessage()); - } - - public function testReplaceDefaultGroupByGroupSequenceArray() - { - $entity = new Entity(); - - $callback1 = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Violation in Group 2'); - }; - $callback2 = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Violation in Group 3'); - }; - - $this->metadata->addConstraint(new Callback([ - 'callback' => function () {}, - 'groups' => 'Group 1', - ])); - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback1, - 'groups' => 'Group 2', - ])); - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback2, - 'groups' => 'Group 3', - ])); - - $sequence = ['Group 1', 'Group 2', 'Group 3', 'Entity']; - $this->metadata->setGroupSequence($sequence); - - $violations = $this->validate($entity, null, 'Default'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Violation in Group 2', $violations[0]->getMessage()); - } - - public function testPropagateDefaultGroupToReferenceWhenReplacingDefaultGroup() - { - $entity = new Entity(); - $entity->reference = new Reference(); - - $callback1 = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Violation in Default group'); - }; - $callback2 = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Violation in group sequence'); - }; - - $this->metadata->addPropertyConstraint('reference', new Valid()); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback1, - 'groups' => 'Default', - ])); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback2, - 'groups' => 'Group 1', - ])); - - $sequence = new GroupSequence(['Group 1', 'Entity']); - $this->metadata->setGroupSequence($sequence); - - $violations = $this->validate($entity, null, 'Default'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Violation in Default group', $violations[0]->getMessage()); - } - - public function testValidateCustomGroupWhenDefaultGroupWasReplaced() - { - $entity = new Entity(); - - $callback1 = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Violation in other group'); - }; - $callback2 = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Violation in group sequence'); - }; - - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback1, - 'groups' => 'Other Group', - ])); - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback2, - 'groups' => 'Group 1', - ])); - - $sequence = new GroupSequence(['Group 1', 'Entity']); - $this->metadata->setGroupSequence($sequence); - - $violations = $this->validate($entity, null, 'Other Group'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(1, $violations); - $this->assertSame('Violation in other group', $violations[0]->getMessage()); - } - - /** - * @dataProvider getTestReplaceDefaultGroup - */ - public function testReplaceDefaultGroup($sequence, array $assertViolations) - { - $entity = new GroupSequenceProviderEntity($sequence); - - $callback1 = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Violation in Group 2'); - }; - $callback2 = function ($value, ExecutionContextInterface $context) { - $context->addViolation('Violation in Group 3'); - }; - - $metadata = new ClassMetadata(\get_class($entity)); - $metadata->addConstraint(new Callback([ - 'callback' => function () {}, - 'groups' => 'Group 1', - ])); - $metadata->addConstraint(new Callback([ - 'callback' => $callback1, - 'groups' => 'Group 2', - ])); - $metadata->addConstraint(new Callback([ - 'callback' => $callback2, - 'groups' => 'Group 3', - ])); - $metadata->setGroupSequenceProvider(true); - - $this->metadataFactory->addMetadata($metadata); - - $violations = $this->validate($entity, null, 'Default'); - - /* @var ConstraintViolationInterface[] $violations */ - $this->assertCount(\count($assertViolations), $violations); - foreach ($assertViolations as $key => $message) { - $this->assertSame($message, $violations[$key]->getMessage()); - } - } - - public function getConstraintMethods() - { - return [ - ['addPropertyConstraint'], - ['addGetterConstraint'], - ]; - } - - public function getTestReplaceDefaultGroup() - { - return [ - [ - 'sequence' => new GroupSequence(['Group 1', 'Group 2', 'Group 3', 'Entity']), - 'assertViolations' => [ - 'Violation in Group 2', - ], - ], - [ - 'sequence' => ['Group 1', 'Group 2', 'Group 3', 'Entity'], - 'assertViolations' => [ - 'Violation in Group 2', - ], - ], - [ - 'sequence' => new GroupSequence(['Group 1', ['Group 2', 'Group 3'], 'Entity']), - 'assertViolations' => [ - 'Violation in Group 2', - 'Violation in Group 3', - ], - ], - [ - 'sequence' => ['Group 1', ['Group 2', 'Group 3'], 'Entity'], - 'assertViolations' => [ - 'Violation in Group 2', - 'Violation in Group 3', - ], - ], - ]; - } -} diff --git a/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php b/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php index 1ebe1534abe23..e925f8da3775f 100644 --- a/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php @@ -11,19 +11,29 @@ namespace Symfony\Component\Validator\Tests\Validator; +use PHPUnit\Framework\TestCase; use Symfony\Component\Translation\IdentityTranslator; +use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\All; +use Symfony\Component\Validator\Constraints\Callback; use Symfony\Component\Validator\Constraints\Collection; +use Symfony\Component\Validator\Constraints\Expression; use Symfony\Component\Validator\Constraints\GroupSequence; +use Symfony\Component\Validator\Constraints\IsFalse; +use Symfony\Component\Validator\Constraints\IsNull; use Symfony\Component\Validator\Constraints\IsTrue; use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\NotNull; use Symfony\Component\Validator\Constraints\Optional; use Symfony\Component\Validator\Constraints\Required; +use Symfony\Component\Validator\Constraints\Traverse; use Symfony\Component\Validator\Constraints\Valid; +use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\ConstraintValidatorFactory; +use Symfony\Component\Validator\ConstraintViolationInterface; use Symfony\Component\Validator\Context\ExecutionContextFactory; +use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Mapping\ClassMetadata; use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface; use Symfony\Component\Validator\Tests\Constraints\Fixtures\ChildA; @@ -31,11 +41,1938 @@ use Symfony\Component\Validator\Tests\Fixtures\Entity; use Symfony\Component\Validator\Tests\Fixtures\EntityParent; use Symfony\Component\Validator\Tests\Fixtures\EntityWithGroupedConstraintOnMethods; +use Symfony\Component\Validator\Tests\Fixtures\FailingConstraint; +use Symfony\Component\Validator\Tests\Fixtures\FakeMetadataFactory; +use Symfony\Component\Validator\Tests\Fixtures\GroupSequenceProviderEntity; +use Symfony\Component\Validator\Tests\Fixtures\Reference; +use Symfony\Component\Validator\Validator\LazyProperty; use Symfony\Component\Validator\Validator\RecursiveValidator; use Symfony\Component\Validator\Validator\ValidatorInterface; -class RecursiveValidatorTest extends AbstractTest +class RecursiveValidatorTest extends TestCase { + private const ENTITY_CLASS = Entity::class; + private const REFERENCE_CLASS = Reference::class; + + /** + * @var FakeMetadataFactory + */ + private $metadataFactory; + + /** + * @var ClassMetadata + */ + private $metadata; + + /** + * @var ClassMetadata + */ + private $referenceMetadata; + + /** + * @var ValidatorInterface + */ + private $validator; + + protected function setUp(): void + { + $this->metadataFactory = new FakeMetadataFactory(); + $this->metadata = new ClassMetadata(self::ENTITY_CLASS); + $this->referenceMetadata = new ClassMetadata(self::REFERENCE_CLASS); + $this->metadataFactory->addMetadata($this->metadata); + $this->metadataFactory->addMetadata($this->referenceMetadata); + $this->metadataFactory->addMetadata(new ClassMetadata(LazyProperty::class)); + + $this->validator = $this->createValidator($this->metadataFactory); + } + + protected function tearDown(): void + { + $this->metadataFactory = null; + $this->metadata = null; + $this->referenceMetadata = null; + } + + protected function validate($value, $constraints = null, $groups = null) + { + return $this->validator->validate($value, $constraints, $groups); + } + + protected function validateProperty($object, $propertyName, $groups = null) + { + return $this->validator->validateProperty($object, $propertyName, $groups); + } + + protected function validatePropertyValue($object, $propertyName, $value, $groups = null) + { + return $this->validator->validatePropertyValue($object, $propertyName, $value, $groups); + } + + public function testValidate() + { + $callback = function ($value, ExecutionContextInterface $context) { + $this->assertNull($context->getClassName()); + $this->assertNull($context->getPropertyName()); + $this->assertSame('', $context->getPropertyPath()); + $this->assertSame('Group', $context->getGroup()); + $this->assertSame('Bernhard', $context->getRoot()); + $this->assertSame('Bernhard', $context->getValue()); + $this->assertSame('Bernhard', $value); + + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $constraint = new Callback([ + 'callback' => $callback, + 'groups' => 'Group', + ]); + + $violations = $this->validate('Bernhard', $constraint, 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Message value', $violations[0]->getMessage()); + $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); + $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); + $this->assertSame('', $violations[0]->getPropertyPath()); + $this->assertSame('Bernhard', $violations[0]->getRoot()); + $this->assertSame('Bernhard', $violations[0]->getInvalidValue()); + $this->assertNull($violations[0]->getPlural()); + $this->assertNull($violations[0]->getCode()); + } + + public function testClassConstraint() + { + $entity = new Entity(); + + $callback = function ($value, ExecutionContextInterface $context) use ($entity) { + $this->assertSame($this::ENTITY_CLASS, $context->getClassName()); + $this->assertNull($context->getPropertyName()); + $this->assertSame('', $context->getPropertyPath()); + $this->assertSame('Group', $context->getGroup()); + $this->assertSame($this->metadata, $context->getMetadata()); + $this->assertSame($entity, $context->getRoot()); + $this->assertSame($entity, $context->getValue()); + $this->assertSame($entity, $value); + + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $this->metadata->addConstraint(new Callback([ + 'callback' => $callback, + 'groups' => 'Group', + ])); + + $violations = $this->validate($entity, null, 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Message value', $violations[0]->getMessage()); + $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); + $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); + $this->assertSame('', $violations[0]->getPropertyPath()); + $this->assertSame($entity, $violations[0]->getRoot()); + $this->assertSame($entity, $violations[0]->getInvalidValue()); + $this->assertNull($violations[0]->getPlural()); + $this->assertNull($violations[0]->getCode()); + } + + public function testPropertyConstraint() + { + $entity = new Entity(); + $entity->firstName = 'Bernhard'; + + $callback = function ($value, ExecutionContextInterface $context) use ($entity) { + $propertyMetadatas = $this->metadata->getPropertyMetadata('firstName'); + + $this->assertSame($this::ENTITY_CLASS, $context->getClassName()); + $this->assertSame('firstName', $context->getPropertyName()); + $this->assertSame('firstName', $context->getPropertyPath()); + $this->assertSame('Group', $context->getGroup()); + $this->assertSame($propertyMetadatas[0], $context->getMetadata()); + $this->assertSame($entity, $context->getRoot()); + $this->assertSame('Bernhard', $context->getValue()); + $this->assertSame('Bernhard', $value); + + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $this->metadata->addPropertyConstraint('firstName', new Callback([ + 'callback' => $callback, + 'groups' => 'Group', + ])); + + $violations = $this->validate($entity, null, 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Message value', $violations[0]->getMessage()); + $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); + $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); + $this->assertSame('firstName', $violations[0]->getPropertyPath()); + $this->assertSame($entity, $violations[0]->getRoot()); + $this->assertSame('Bernhard', $violations[0]->getInvalidValue()); + $this->assertNull($violations[0]->getPlural()); + $this->assertNull($violations[0]->getCode()); + } + + public function testGetterConstraint() + { + $entity = new Entity(); + $entity->setLastName('Schussek'); + + $callback = function ($value, ExecutionContextInterface $context) use ($entity) { + $propertyMetadatas = $this->metadata->getPropertyMetadata('lastName'); + + $this->assertSame($this::ENTITY_CLASS, $context->getClassName()); + $this->assertSame('lastName', $context->getPropertyName()); + $this->assertSame('lastName', $context->getPropertyPath()); + $this->assertSame('Group', $context->getGroup()); + $this->assertSame($propertyMetadatas[0], $context->getMetadata()); + $this->assertSame($entity, $context->getRoot()); + $this->assertSame('Schussek', $context->getValue()); + $this->assertSame('Schussek', $value); + + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $this->metadata->addGetterConstraint('lastName', new Callback([ + 'callback' => $callback, + 'groups' => 'Group', + ])); + + $violations = $this->validate($entity, null, 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Message value', $violations[0]->getMessage()); + $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); + $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); + $this->assertSame('lastName', $violations[0]->getPropertyPath()); + $this->assertSame($entity, $violations[0]->getRoot()); + $this->assertSame('Schussek', $violations[0]->getInvalidValue()); + $this->assertNull($violations[0]->getPlural()); + $this->assertNull($violations[0]->getCode()); + } + + public function testArray() + { + $entity = new Entity(); + $array = ['key' => $entity]; + + $callback = function ($value, ExecutionContextInterface $context) use ($entity, $array) { + $this->assertSame($this::ENTITY_CLASS, $context->getClassName()); + $this->assertNull($context->getPropertyName()); + $this->assertSame('[key]', $context->getPropertyPath()); + $this->assertSame('Group', $context->getGroup()); + $this->assertSame($this->metadata, $context->getMetadata()); + $this->assertSame($array, $context->getRoot()); + $this->assertSame($entity, $context->getValue()); + $this->assertSame($entity, $value); + + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $this->metadata->addConstraint(new Callback([ + 'callback' => $callback, + 'groups' => 'Group', + ])); + + $violations = $this->validate($array, null, 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Message value', $violations[0]->getMessage()); + $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); + $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); + $this->assertSame('[key]', $violations[0]->getPropertyPath()); + $this->assertSame($array, $violations[0]->getRoot()); + $this->assertSame($entity, $violations[0]->getInvalidValue()); + $this->assertNull($violations[0]->getPlural()); + $this->assertNull($violations[0]->getCode()); + } + + public function testRecursiveArray() + { + $entity = new Entity(); + $array = [2 => ['key' => $entity]]; + + $callback = function ($value, ExecutionContextInterface $context) use ($entity, $array) { + $this->assertSame($this::ENTITY_CLASS, $context->getClassName()); + $this->assertNull($context->getPropertyName()); + $this->assertSame('[2][key]', $context->getPropertyPath()); + $this->assertSame('Group', $context->getGroup()); + $this->assertSame($this->metadata, $context->getMetadata()); + $this->assertSame($array, $context->getRoot()); + $this->assertSame($entity, $context->getValue()); + $this->assertSame($entity, $value); + + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $this->metadata->addConstraint(new Callback([ + 'callback' => $callback, + 'groups' => 'Group', + ])); + + $violations = $this->validate($array, null, 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Message value', $violations[0]->getMessage()); + $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); + $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); + $this->assertSame('[2][key]', $violations[0]->getPropertyPath()); + $this->assertSame($array, $violations[0]->getRoot()); + $this->assertSame($entity, $violations[0]->getInvalidValue()); + $this->assertNull($violations[0]->getPlural()); + $this->assertNull($violations[0]->getCode()); + } + + public function testTraversable() + { + $entity = new Entity(); + $traversable = new \ArrayIterator(['key' => $entity]); + + $callback = function ($value, ExecutionContextInterface $context) use ($entity, $traversable) { + $this->assertSame($this::ENTITY_CLASS, $context->getClassName()); + $this->assertNull($context->getPropertyName()); + $this->assertSame('[key]', $context->getPropertyPath()); + $this->assertSame('Group', $context->getGroup()); + $this->assertSame($this->metadata, $context->getMetadata()); + $this->assertSame($traversable, $context->getRoot()); + $this->assertSame($entity, $context->getValue()); + $this->assertSame($entity, $value); + + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $this->metadata->addConstraint(new Callback([ + 'callback' => $callback, + 'groups' => 'Group', + ])); + + $violations = $this->validate($traversable, null, 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Message value', $violations[0]->getMessage()); + $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); + $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); + $this->assertSame('[key]', $violations[0]->getPropertyPath()); + $this->assertSame($traversable, $violations[0]->getRoot()); + $this->assertSame($entity, $violations[0]->getInvalidValue()); + $this->assertNull($violations[0]->getPlural()); + $this->assertNull($violations[0]->getCode()); + } + + public function testRecursiveTraversable() + { + $entity = new Entity(); + $traversable = new \ArrayIterator([ + 2 => new \ArrayIterator(['key' => $entity]), + ]); + + $callback = function ($value, ExecutionContextInterface $context) use ($entity, $traversable) { + $this->assertSame($this::ENTITY_CLASS, $context->getClassName()); + $this->assertNull($context->getPropertyName()); + $this->assertSame('[2][key]', $context->getPropertyPath()); + $this->assertSame('Group', $context->getGroup()); + $this->assertSame($this->metadata, $context->getMetadata()); + $this->assertSame($traversable, $context->getRoot()); + $this->assertSame($entity, $context->getValue()); + $this->assertSame($entity, $value); + + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $this->metadata->addConstraint(new Callback([ + 'callback' => $callback, + 'groups' => 'Group', + ])); + + $violations = $this->validate($traversable, null, 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Message value', $violations[0]->getMessage()); + $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); + $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); + $this->assertSame('[2][key]', $violations[0]->getPropertyPath()); + $this->assertSame($traversable, $violations[0]->getRoot()); + $this->assertSame($entity, $violations[0]->getInvalidValue()); + $this->assertNull($violations[0]->getPlural()); + $this->assertNull($violations[0]->getCode()); + } + + public function testReferenceClassConstraint() + { + $entity = new Entity(); + $entity->reference = new Reference(); + + $callback = function ($value, ExecutionContextInterface $context) use ($entity) { + $this->assertSame($this::REFERENCE_CLASS, $context->getClassName()); + $this->assertNull($context->getPropertyName()); + $this->assertSame('reference', $context->getPropertyPath()); + $this->assertSame('Group', $context->getGroup()); + $this->assertSame($this->referenceMetadata, $context->getMetadata()); + $this->assertSame($entity, $context->getRoot()); + $this->assertSame($entity->reference, $context->getValue()); + $this->assertSame($entity->reference, $value); + + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $this->metadata->addPropertyConstraint('reference', new Valid()); + $this->referenceMetadata->addConstraint(new Callback([ + 'callback' => $callback, + 'groups' => 'Group', + ])); + + $violations = $this->validate($entity, null, 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Message value', $violations[0]->getMessage()); + $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); + $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); + $this->assertSame('reference', $violations[0]->getPropertyPath()); + $this->assertSame($entity, $violations[0]->getRoot()); + $this->assertSame($entity->reference, $violations[0]->getInvalidValue()); + $this->assertNull($violations[0]->getPlural()); + $this->assertNull($violations[0]->getCode()); + } + + public function testReferencePropertyConstraint() + { + $entity = new Entity(); + $entity->reference = new Reference(); + $entity->reference->value = 'Foobar'; + + $callback = function ($value, ExecutionContextInterface $context) use ($entity) { + $propertyMetadatas = $this->referenceMetadata->getPropertyMetadata('value'); + + $this->assertSame($this::REFERENCE_CLASS, $context->getClassName()); + $this->assertSame('value', $context->getPropertyName()); + $this->assertSame('reference.value', $context->getPropertyPath()); + $this->assertSame('Group', $context->getGroup()); + $this->assertSame($propertyMetadatas[0], $context->getMetadata()); + $this->assertSame($entity, $context->getRoot()); + $this->assertSame('Foobar', $context->getValue()); + $this->assertSame('Foobar', $value); + + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $this->metadata->addPropertyConstraint('reference', new Valid()); + $this->referenceMetadata->addPropertyConstraint('value', new Callback([ + 'callback' => $callback, + 'groups' => 'Group', + ])); + + $violations = $this->validate($entity, null, 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Message value', $violations[0]->getMessage()); + $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); + $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); + $this->assertSame('reference.value', $violations[0]->getPropertyPath()); + $this->assertSame($entity, $violations[0]->getRoot()); + $this->assertSame('Foobar', $violations[0]->getInvalidValue()); + $this->assertNull($violations[0]->getPlural()); + $this->assertNull($violations[0]->getCode()); + } + + public function testReferenceGetterConstraint() + { + $entity = new Entity(); + $entity->reference = new Reference(); + $entity->reference->setPrivateValue('Bamboo'); + + $callback = function ($value, ExecutionContextInterface $context) use ($entity) { + $propertyMetadatas = $this->referenceMetadata->getPropertyMetadata('privateValue'); + + $this->assertSame($this::REFERENCE_CLASS, $context->getClassName()); + $this->assertSame('privateValue', $context->getPropertyName()); + $this->assertSame('reference.privateValue', $context->getPropertyPath()); + $this->assertSame('Group', $context->getGroup()); + $this->assertSame($propertyMetadatas[0], $context->getMetadata()); + $this->assertSame($entity, $context->getRoot()); + $this->assertSame('Bamboo', $context->getValue()); + $this->assertSame('Bamboo', $value); + + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $this->metadata->addPropertyConstraint('reference', new Valid()); + $this->referenceMetadata->addPropertyConstraint('privateValue', new Callback([ + 'callback' => $callback, + 'groups' => 'Group', + ])); + + $violations = $this->validate($entity, null, 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Message value', $violations[0]->getMessage()); + $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); + $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); + $this->assertSame('reference.privateValue', $violations[0]->getPropertyPath()); + $this->assertSame($entity, $violations[0]->getRoot()); + $this->assertSame('Bamboo', $violations[0]->getInvalidValue()); + $this->assertNull($violations[0]->getPlural()); + $this->assertNull($violations[0]->getCode()); + } + + public function testsIgnoreNullReference() + { + $entity = new Entity(); + $entity->reference = null; + + $this->metadata->addPropertyConstraint('reference', new Valid()); + + $violations = $this->validate($entity); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(0, $violations); + } + + public function testFailOnScalarReferences() + { + $this->expectException('Symfony\Component\Validator\Exception\NoSuchMetadataException'); + $entity = new Entity(); + $entity->reference = 'string'; + + $this->metadata->addPropertyConstraint('reference', new Valid()); + + $this->validate($entity); + } + + /** + * @dataProvider getConstraintMethods + */ + public function testArrayReference($constraintMethod) + { + $entity = new Entity(); + $entity->reference = ['key' => new Reference()]; + + $callback = function ($value, ExecutionContextInterface $context) use ($entity) { + $this->assertSame($this::REFERENCE_CLASS, $context->getClassName()); + $this->assertNull($context->getPropertyName()); + $this->assertSame('reference[key]', $context->getPropertyPath()); + $this->assertSame('Group', $context->getGroup()); + $this->assertSame($this->referenceMetadata, $context->getMetadata()); + $this->assertSame($entity, $context->getRoot()); + $this->assertSame($entity->reference['key'], $context->getValue()); + $this->assertSame($entity->reference['key'], $value); + + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $this->metadata->$constraintMethod('reference', new Valid()); + $this->referenceMetadata->addConstraint(new Callback([ + 'callback' => $callback, + 'groups' => 'Group', + ])); + + $violations = $this->validate($entity, null, 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Message value', $violations[0]->getMessage()); + $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); + $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); + $this->assertSame('reference[key]', $violations[0]->getPropertyPath()); + $this->assertSame($entity, $violations[0]->getRoot()); + $this->assertSame($entity->reference['key'], $violations[0]->getInvalidValue()); + $this->assertNull($violations[0]->getPlural()); + $this->assertNull($violations[0]->getCode()); + } + + /** + * @dataProvider getConstraintMethods + */ + public function testRecursiveArrayReference($constraintMethod) + { + $entity = new Entity(); + $entity->reference = [2 => ['key' => new Reference()]]; + + $callback = function ($value, ExecutionContextInterface $context) use ($entity) { + $this->assertSame($this::REFERENCE_CLASS, $context->getClassName()); + $this->assertNull($context->getPropertyName()); + $this->assertSame('reference[2][key]', $context->getPropertyPath()); + $this->assertSame('Group', $context->getGroup()); + $this->assertSame($this->referenceMetadata, $context->getMetadata()); + $this->assertSame($entity, $context->getRoot()); + $this->assertSame($entity->reference[2]['key'], $context->getValue()); + $this->assertSame($entity->reference[2]['key'], $value); + + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $this->metadata->$constraintMethod('reference', new Valid()); + $this->referenceMetadata->addConstraint(new Callback([ + 'callback' => $callback, + 'groups' => 'Group', + ])); + + $violations = $this->validate($entity, null, 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Message value', $violations[0]->getMessage()); + $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); + $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); + $this->assertSame('reference[2][key]', $violations[0]->getPropertyPath()); + $this->assertSame($entity, $violations[0]->getRoot()); + $this->assertSame($entity->reference[2]['key'], $violations[0]->getInvalidValue()); + $this->assertNull($violations[0]->getPlural()); + $this->assertNull($violations[0]->getCode()); + } + + public function testOnlyCascadedArraysAreTraversed() + { + $entity = new Entity(); + $entity->reference = ['key' => new Reference()]; + + $callback = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $this->metadata->addPropertyConstraint('reference', new Callback([ + 'callback' => function () {}, + 'groups' => 'Group', + ])); + $this->referenceMetadata->addConstraint(new Callback([ + 'callback' => $callback, + 'groups' => 'Group', + ])); + + $violations = $this->validate($entity, null, 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(0, $violations); + } + + /** + * @dataProvider getConstraintMethods + */ + public function testArrayTraversalCannotBeDisabled($constraintMethod) + { + $entity = new Entity(); + $entity->reference = ['key' => new Reference()]; + + $callback = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $this->metadata->$constraintMethod('reference', new Valid([ + 'traverse' => false, + ])); + $this->referenceMetadata->addConstraint(new Callback($callback)); + + $violations = $this->validate($entity); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + } + + /** + * @dataProvider getConstraintMethods + */ + public function testRecursiveArrayTraversalCannotBeDisabled($constraintMethod) + { + $entity = new Entity(); + $entity->reference = [2 => ['key' => new Reference()]]; + + $callback = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $this->metadata->$constraintMethod('reference', new Valid([ + 'traverse' => false, + ])); + + $this->referenceMetadata->addConstraint(new Callback($callback)); + + $violations = $this->validate($entity); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + } + + /** + * @dataProvider getConstraintMethods + */ + public function testIgnoreScalarsDuringArrayTraversal($constraintMethod) + { + $entity = new Entity(); + $entity->reference = ['string', 1234]; + + $this->metadata->$constraintMethod('reference', new Valid()); + + $violations = $this->validate($entity); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(0, $violations); + } + + /** + * @dataProvider getConstraintMethods + */ + public function testIgnoreNullDuringArrayTraversal($constraintMethod) + { + $entity = new Entity(); + $entity->reference = [null]; + + $this->metadata->$constraintMethod('reference', new Valid()); + + $violations = $this->validate($entity); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(0, $violations); + } + + public function testTraversableReference() + { + $entity = new Entity(); + $entity->reference = new \ArrayIterator(['key' => new Reference()]); + + $callback = function ($value, ExecutionContextInterface $context) use ($entity) { + $this->assertSame($this::REFERENCE_CLASS, $context->getClassName()); + $this->assertNull($context->getPropertyName()); + $this->assertSame('reference[key]', $context->getPropertyPath()); + $this->assertSame('Group', $context->getGroup()); + $this->assertSame($this->referenceMetadata, $context->getMetadata()); + $this->assertSame($entity, $context->getRoot()); + $this->assertSame($entity->reference['key'], $context->getValue()); + $this->assertSame($entity->reference['key'], $value); + + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $this->metadata->addPropertyConstraint('reference', new Valid()); + $this->referenceMetadata->addConstraint(new Callback([ + 'callback' => $callback, + 'groups' => 'Group', + ])); + + $violations = $this->validate($entity, null, 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Message value', $violations[0]->getMessage()); + $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); + $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); + $this->assertSame('reference[key]', $violations[0]->getPropertyPath()); + $this->assertSame($entity, $violations[0]->getRoot()); + $this->assertSame($entity->reference['key'], $violations[0]->getInvalidValue()); + $this->assertNull($violations[0]->getPlural()); + $this->assertNull($violations[0]->getCode()); + } + + public function testDisableTraversableTraversal() + { + $entity = new Entity(); + $entity->reference = new \ArrayIterator(['key' => new Reference()]); + + $callback = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $this->metadataFactory->addMetadata(new ClassMetadata('ArrayIterator')); + $this->metadata->addPropertyConstraint('reference', new Valid([ + 'traverse' => false, + ])); + $this->referenceMetadata->addConstraint(new Callback($callback)); + + $violations = $this->validate($entity); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(0, $violations); + } + + public function testMetadataMustExistIfTraversalIsDisabled() + { + $this->expectException('Symfony\Component\Validator\Exception\NoSuchMetadataException'); + $entity = new Entity(); + $entity->reference = new \ArrayIterator(); + + $this->metadata->addPropertyConstraint('reference', new Valid([ + 'traverse' => false, + ])); + + $this->validate($entity); + } + + public function testEnableRecursiveTraversableTraversal() + { + $entity = new Entity(); + $entity->reference = new \ArrayIterator([ + 2 => new \ArrayIterator(['key' => new Reference()]), + ]); + + $callback = function ($value, ExecutionContextInterface $context) use ($entity) { + $this->assertSame($this::REFERENCE_CLASS, $context->getClassName()); + $this->assertNull($context->getPropertyName()); + $this->assertSame('reference[2][key]', $context->getPropertyPath()); + $this->assertSame('Group', $context->getGroup()); + $this->assertSame($this->referenceMetadata, $context->getMetadata()); + $this->assertSame($entity, $context->getRoot()); + $this->assertSame($entity->reference[2]['key'], $context->getValue()); + $this->assertSame($entity->reference[2]['key'], $value); + + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $this->metadata->addPropertyConstraint('reference', new Valid([ + 'traverse' => true, + ])); + $this->referenceMetadata->addConstraint(new Callback([ + 'callback' => $callback, + 'groups' => 'Group', + ])); + + $violations = $this->validate($entity, null, 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Message value', $violations[0]->getMessage()); + $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); + $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); + $this->assertSame('reference[2][key]', $violations[0]->getPropertyPath()); + $this->assertSame($entity, $violations[0]->getRoot()); + $this->assertSame($entity->reference[2]['key'], $violations[0]->getInvalidValue()); + $this->assertNull($violations[0]->getPlural()); + $this->assertNull($violations[0]->getCode()); + } + + public function testValidateProperty() + { + $entity = new Entity(); + $entity->firstName = 'Bernhard'; + $entity->setLastName('Schussek'); + + $callback1 = function ($value, ExecutionContextInterface $context) use ($entity) { + $propertyMetadatas = $this->metadata->getPropertyMetadata('firstName'); + + $this->assertSame($this::ENTITY_CLASS, $context->getClassName()); + $this->assertSame('firstName', $context->getPropertyName()); + $this->assertSame('firstName', $context->getPropertyPath()); + $this->assertSame('Group', $context->getGroup()); + $this->assertSame($propertyMetadatas[0], $context->getMetadata()); + $this->assertSame($entity, $context->getRoot()); + $this->assertSame('Bernhard', $context->getValue()); + $this->assertSame('Bernhard', $value); + + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $callback2 = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Other violation'); + }; + + $this->metadata->addPropertyConstraint('firstName', new Callback([ + 'callback' => $callback1, + 'groups' => 'Group', + ])); + $this->metadata->addPropertyConstraint('lastName', new Callback([ + 'callback' => $callback2, + 'groups' => 'Group', + ])); + + $violations = $this->validateProperty($entity, 'firstName', 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Message value', $violations[0]->getMessage()); + $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); + $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); + $this->assertSame('firstName', $violations[0]->getPropertyPath()); + $this->assertSame($entity, $violations[0]->getRoot()); + $this->assertSame('Bernhard', $violations[0]->getInvalidValue()); + $this->assertNull($violations[0]->getPlural()); + $this->assertNull($violations[0]->getCode()); + } + + /** + * https://github.com/symfony/symfony/issues/11604. + */ + public function testValidatePropertyWithoutConstraints() + { + $entity = new Entity(); + $violations = $this->validateProperty($entity, 'lastName'); + + $this->assertCount(0, $violations, '->validateProperty() returns no violations if no constraints have been configured for the property being validated'); + } + + public function testValidatePropertyValue() + { + $entity = new Entity(); + $entity->setLastName('Schussek'); + + $callback1 = function ($value, ExecutionContextInterface $context) use ($entity) { + $propertyMetadatas = $this->metadata->getPropertyMetadata('firstName'); + + $this->assertSame($this::ENTITY_CLASS, $context->getClassName()); + $this->assertSame('firstName', $context->getPropertyName()); + $this->assertSame('firstName', $context->getPropertyPath()); + $this->assertSame('Group', $context->getGroup()); + $this->assertSame($propertyMetadatas[0], $context->getMetadata()); + $this->assertSame($entity, $context->getRoot()); + $this->assertSame('Bernhard', $context->getValue()); + $this->assertSame('Bernhard', $value); + + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $callback2 = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Other violation'); + }; + + $this->metadata->addPropertyConstraint('firstName', new Callback([ + 'callback' => $callback1, + 'groups' => 'Group', + ])); + $this->metadata->addPropertyConstraint('lastName', new Callback([ + 'callback' => $callback2, + 'groups' => 'Group', + ])); + + $violations = $this->validatePropertyValue( + $entity, + 'firstName', + 'Bernhard', + 'Group' + ); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Message value', $violations[0]->getMessage()); + $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); + $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); + $this->assertSame('firstName', $violations[0]->getPropertyPath()); + $this->assertSame($entity, $violations[0]->getRoot()); + $this->assertSame('Bernhard', $violations[0]->getInvalidValue()); + $this->assertNull($violations[0]->getPlural()); + $this->assertNull($violations[0]->getCode()); + } + + public function testValidatePropertyValueWithClassName() + { + $callback1 = function ($value, ExecutionContextInterface $context) { + $propertyMetadatas = $this->metadata->getPropertyMetadata('firstName'); + + $this->assertSame($this::ENTITY_CLASS, $context->getClassName()); + $this->assertSame('firstName', $context->getPropertyName()); + $this->assertSame('', $context->getPropertyPath()); + $this->assertSame('Group', $context->getGroup()); + $this->assertSame($propertyMetadatas[0], $context->getMetadata()); + $this->assertSame('Bernhard', $context->getRoot()); + $this->assertSame('Bernhard', $context->getValue()); + $this->assertSame('Bernhard', $value); + + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $callback2 = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Other violation'); + }; + + $this->metadata->addPropertyConstraint('firstName', new Callback([ + 'callback' => $callback1, + 'groups' => 'Group', + ])); + $this->metadata->addPropertyConstraint('lastName', new Callback([ + 'callback' => $callback2, + 'groups' => 'Group', + ])); + + $violations = $this->validatePropertyValue( + self::ENTITY_CLASS, + 'firstName', + 'Bernhard', + 'Group' + ); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Message value', $violations[0]->getMessage()); + $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); + $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); + $this->assertSame('', $violations[0]->getPropertyPath()); + $this->assertSame('Bernhard', $violations[0]->getRoot()); + $this->assertSame('Bernhard', $violations[0]->getInvalidValue()); + $this->assertNull($violations[0]->getPlural()); + $this->assertNull($violations[0]->getCode()); + } + + /** + * https://github.com/symfony/symfony/issues/11604. + */ + public function testValidatePropertyValueWithoutConstraints() + { + $entity = new Entity(); + $violations = $this->validatePropertyValue($entity, 'lastName', 'foo'); + + $this->assertCount(0, $violations, '->validatePropertyValue() returns no violations if no constraints have been configured for the property being validated'); + } + + public function testValidateObjectOnlyOncePerGroup() + { + $entity = new Entity(); + $entity->reference = new Reference(); + $entity->reference2 = $entity->reference; + + $callback = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Message'); + }; + + $this->metadata->addPropertyConstraint('reference', new Valid()); + $this->metadata->addPropertyConstraint('reference2', new Valid()); + $this->referenceMetadata->addConstraint(new Callback($callback)); + + $violations = $this->validate($entity); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + } + + public function testValidateDifferentObjectsSeparately() + { + $entity = new Entity(); + $entity->reference = new Reference(); + $entity->reference2 = new Reference(); + + $callback = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Message'); + }; + + $this->metadata->addPropertyConstraint('reference', new Valid()); + $this->metadata->addPropertyConstraint('reference2', new Valid()); + $this->referenceMetadata->addConstraint(new Callback($callback)); + + $violations = $this->validate($entity); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(2, $violations); + } + + public function testValidateSingleGroup() + { + $entity = new Entity(); + + $callback = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Message'); + }; + + $this->metadata->addConstraint(new Callback([ + 'callback' => $callback, + 'groups' => 'Group 1', + ])); + $this->metadata->addConstraint(new Callback([ + 'callback' => $callback, + 'groups' => 'Group 2', + ])); + + $violations = $this->validate($entity, null, 'Group 2'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + } + + public function testValidateMultipleGroups() + { + $entity = new Entity(); + + $callback = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Message'); + }; + + $this->metadata->addConstraint(new Callback([ + 'callback' => $callback, + 'groups' => 'Group 1', + ])); + $this->metadata->addConstraint(new Callback([ + 'callback' => $callback, + 'groups' => 'Group 2', + ])); + + $violations = $this->validate($entity, null, ['Group 1', 'Group 2']); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(2, $violations); + } + + public function testReplaceDefaultGroupByGroupSequenceObject() + { + $entity = new Entity(); + + $callback1 = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Violation in Group 2'); + }; + $callback2 = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Violation in Group 3'); + }; + + $this->metadata->addConstraint(new Callback([ + 'callback' => function () {}, + 'groups' => 'Group 1', + ])); + $this->metadata->addConstraint(new Callback([ + 'callback' => $callback1, + 'groups' => 'Group 2', + ])); + $this->metadata->addConstraint(new Callback([ + 'callback' => $callback2, + 'groups' => 'Group 3', + ])); + + $sequence = new GroupSequence(['Group 1', 'Group 2', 'Group 3', 'Entity']); + $this->metadata->setGroupSequence($sequence); + + $violations = $this->validate($entity, null, 'Default'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Violation in Group 2', $violations[0]->getMessage()); + } + + public function testReplaceDefaultGroupByGroupSequenceArray() + { + $entity = new Entity(); + + $callback1 = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Violation in Group 2'); + }; + $callback2 = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Violation in Group 3'); + }; + + $this->metadata->addConstraint(new Callback([ + 'callback' => function () {}, + 'groups' => 'Group 1', + ])); + $this->metadata->addConstraint(new Callback([ + 'callback' => $callback1, + 'groups' => 'Group 2', + ])); + $this->metadata->addConstraint(new Callback([ + 'callback' => $callback2, + 'groups' => 'Group 3', + ])); + + $sequence = ['Group 1', 'Group 2', 'Group 3', 'Entity']; + $this->metadata->setGroupSequence($sequence); + + $violations = $this->validate($entity, null, 'Default'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Violation in Group 2', $violations[0]->getMessage()); + } + + public function testPropagateDefaultGroupToReferenceWhenReplacingDefaultGroup() + { + $entity = new Entity(); + $entity->reference = new Reference(); + + $callback1 = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Violation in Default group'); + }; + $callback2 = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Violation in group sequence'); + }; + + $this->metadata->addPropertyConstraint('reference', new Valid()); + $this->referenceMetadata->addConstraint(new Callback([ + 'callback' => $callback1, + 'groups' => 'Default', + ])); + $this->referenceMetadata->addConstraint(new Callback([ + 'callback' => $callback2, + 'groups' => 'Group 1', + ])); + + $sequence = new GroupSequence(['Group 1', 'Entity']); + $this->metadata->setGroupSequence($sequence); + + $violations = $this->validate($entity, null, 'Default'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Violation in Default group', $violations[0]->getMessage()); + } + + public function testValidateCustomGroupWhenDefaultGroupWasReplaced() + { + $entity = new Entity(); + + $callback1 = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Violation in other group'); + }; + $callback2 = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Violation in group sequence'); + }; + + $this->metadata->addConstraint(new Callback([ + 'callback' => $callback1, + 'groups' => 'Other Group', + ])); + $this->metadata->addConstraint(new Callback([ + 'callback' => $callback2, + 'groups' => 'Group 1', + ])); + + $sequence = new GroupSequence(['Group 1', 'Entity']); + $this->metadata->setGroupSequence($sequence); + + $violations = $this->validate($entity, null, 'Other Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Violation in other group', $violations[0]->getMessage()); + } + + /** + * @dataProvider getTestReplaceDefaultGroup + */ + public function testReplaceDefaultGroup($sequence, array $assertViolations) + { + $entity = new GroupSequenceProviderEntity($sequence); + + $callback1 = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Violation in Group 2'); + }; + $callback2 = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Violation in Group 3'); + }; + + $metadata = new ClassMetadata(\get_class($entity)); + $metadata->addConstraint(new Callback([ + 'callback' => function () {}, + 'groups' => 'Group 1', + ])); + $metadata->addConstraint(new Callback([ + 'callback' => $callback1, + 'groups' => 'Group 2', + ])); + $metadata->addConstraint(new Callback([ + 'callback' => $callback2, + 'groups' => 'Group 3', + ])); + $metadata->setGroupSequenceProvider(true); + + $this->metadataFactory->addMetadata($metadata); + + $violations = $this->validate($entity, null, 'Default'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(\count($assertViolations), $violations); + foreach ($assertViolations as $key => $message) { + $this->assertSame($message, $violations[$key]->getMessage()); + } + } + + public function getConstraintMethods() + { + return [ + ['addPropertyConstraint'], + ['addGetterConstraint'], + ]; + } + + public function getTestReplaceDefaultGroup() + { + return [ + [ + 'sequence' => new GroupSequence(['Group 1', 'Group 2', 'Group 3', 'Entity']), + 'assertViolations' => [ + 'Violation in Group 2', + ], + ], + [ + 'sequence' => ['Group 1', 'Group 2', 'Group 3', 'Entity'], + 'assertViolations' => [ + 'Violation in Group 2', + ], + ], + [ + 'sequence' => new GroupSequence(['Group 1', ['Group 2', 'Group 3'], 'Entity']), + 'assertViolations' => [ + 'Violation in Group 2', + 'Violation in Group 3', + ], + ], + [ + 'sequence' => ['Group 1', ['Group 2', 'Group 3'], 'Entity'], + 'assertViolations' => [ + 'Violation in Group 2', + 'Violation in Group 3', + ], + ], + ]; + } + + public function testValidateConstraintWithoutGroup() + { + $violations = $this->validator->validate(null, new NotNull()); + + $this->assertCount(1, $violations); + } + + public function testValidateWithEmptyArrayAsConstraint() + { + $violations = $this->validator->validate('value', []); + $this->assertCount(0, $violations); + } + + public function testGroupSequenceAbortsAfterFailedGroup() + { + $entity = new Entity(); + + $callback1 = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Message 1'); + }; + $callback2 = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Message 2'); + }; + + $this->metadata->addConstraint(new Callback([ + 'callback' => function () {}, + 'groups' => 'Group 1', + ])); + $this->metadata->addConstraint(new Callback([ + 'callback' => $callback1, + 'groups' => 'Group 2', + ])); + $this->metadata->addConstraint(new Callback([ + 'callback' => $callback2, + 'groups' => 'Group 3', + ])); + + $sequence = new GroupSequence(['Group 1', 'Group 2', 'Group 3']); + $violations = $this->validator->validate($entity, new Valid(), $sequence); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Message 1', $violations[0]->getMessage()); + } + + public function testGroupSequenceIncludesReferences() + { + $entity = new Entity(); + $entity->reference = new Reference(); + + $callback1 = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Reference violation 1'); + }; + $callback2 = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Reference violation 2'); + }; + + $this->metadata->addPropertyConstraint('reference', new Valid()); + $this->referenceMetadata->addConstraint(new Callback([ + 'callback' => $callback1, + 'groups' => 'Group 1', + ])); + $this->referenceMetadata->addConstraint(new Callback([ + 'callback' => $callback2, + 'groups' => 'Group 2', + ])); + + $sequence = new GroupSequence(['Group 1', 'Entity']); + $violations = $this->validator->validate($entity, new Valid(), $sequence); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Reference violation 1', $violations[0]->getMessage()); + } + + public function testValidateInSeparateContext() + { + $entity = new Entity(); + $entity->reference = new Reference(); + + $callback1 = function ($value, ExecutionContextInterface $context) use ($entity) { + $violations = $context + ->getValidator() + // Since the validator is not context aware, the group must + // be passed explicitly + ->validate($value->reference, new Valid(), 'Group') + ; + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Message value', $violations[0]->getMessage()); + $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); + $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); + $this->assertSame('', $violations[0]->getPropertyPath()); + + // The root is different as we're in a new context + $this->assertSame($entity->reference, $violations[0]->getRoot()); + $this->assertSame($entity->reference, $violations[0]->getInvalidValue()); + $this->assertNull($violations[0]->getPlural()); + $this->assertNull($violations[0]->getCode()); + + // Verify that this method is called + $context->addViolation('Separate violation'); + }; + + $callback2 = function ($value, ExecutionContextInterface $context) use ($entity) { + $this->assertSame($this::REFERENCE_CLASS, $context->getClassName()); + $this->assertNull($context->getPropertyName()); + $this->assertSame('', $context->getPropertyPath()); + $this->assertSame('Group', $context->getGroup()); + $this->assertSame($this->referenceMetadata, $context->getMetadata()); + $this->assertSame($entity->reference, $context->getRoot()); + $this->assertSame($entity->reference, $context->getValue()); + $this->assertSame($entity->reference, $value); + + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $this->metadata->addConstraint(new Callback([ + 'callback' => $callback1, + 'groups' => 'Group', + ])); + $this->referenceMetadata->addConstraint(new Callback([ + 'callback' => $callback2, + 'groups' => 'Group', + ])); + + $violations = $this->validator->validate($entity, new Valid(), 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Separate violation', $violations[0]->getMessage()); + } + + public function testValidateInContext() + { + $entity = new Entity(); + $entity->reference = new Reference(); + + $callback1 = function ($value, ExecutionContextInterface $context) { + $previousValue = $context->getValue(); + $previousObject = $context->getObject(); + $previousMetadata = $context->getMetadata(); + $previousPath = $context->getPropertyPath(); + $previousGroup = $context->getGroup(); + + $context + ->getValidator() + ->inContext($context) + ->atPath('subpath') + ->validate($value->reference) + ; + + // context changes shouldn't leak out of the validate() call + $this->assertSame($previousValue, $context->getValue()); + $this->assertSame($previousObject, $context->getObject()); + $this->assertSame($previousMetadata, $context->getMetadata()); + $this->assertSame($previousPath, $context->getPropertyPath()); + $this->assertSame($previousGroup, $context->getGroup()); + }; + + $callback2 = function ($value, ExecutionContextInterface $context) use ($entity) { + $this->assertSame($this::REFERENCE_CLASS, $context->getClassName()); + $this->assertNull($context->getPropertyName()); + $this->assertSame('subpath', $context->getPropertyPath()); + $this->assertSame('Group', $context->getGroup()); + $this->assertSame($this->referenceMetadata, $context->getMetadata()); + $this->assertSame($entity, $context->getRoot()); + $this->assertSame($entity->reference, $context->getValue()); + $this->assertSame($entity->reference, $value); + + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $this->metadata->addConstraint(new Callback([ + 'callback' => $callback1, + 'groups' => 'Group', + ])); + $this->referenceMetadata->addConstraint(new Callback([ + 'callback' => $callback2, + 'groups' => 'Group', + ])); + + $violations = $this->validator->validate($entity, new Valid(), 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Message value', $violations[0]->getMessage()); + $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); + $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); + $this->assertSame('subpath', $violations[0]->getPropertyPath()); + $this->assertSame($entity, $violations[0]->getRoot()); + $this->assertSame($entity->reference, $violations[0]->getInvalidValue()); + $this->assertNull($violations[0]->getPlural()); + $this->assertNull($violations[0]->getCode()); + } + + public function testValidateArrayInContext() + { + $entity = new Entity(); + $entity->reference = new Reference(); + + $callback1 = function ($value, ExecutionContextInterface $context) { + $previousValue = $context->getValue(); + $previousObject = $context->getObject(); + $previousMetadata = $context->getMetadata(); + $previousPath = $context->getPropertyPath(); + $previousGroup = $context->getGroup(); + + $context + ->getValidator() + ->inContext($context) + ->atPath('subpath') + ->validate(['key' => $value->reference]) + ; + + // context changes shouldn't leak out of the validate() call + $this->assertSame($previousValue, $context->getValue()); + $this->assertSame($previousObject, $context->getObject()); + $this->assertSame($previousMetadata, $context->getMetadata()); + $this->assertSame($previousPath, $context->getPropertyPath()); + $this->assertSame($previousGroup, $context->getGroup()); + }; + + $callback2 = function ($value, ExecutionContextInterface $context) use ($entity) { + $this->assertSame($this::REFERENCE_CLASS, $context->getClassName()); + $this->assertNull($context->getPropertyName()); + $this->assertSame('subpath[key]', $context->getPropertyPath()); + $this->assertSame('Group', $context->getGroup()); + $this->assertSame($this->referenceMetadata, $context->getMetadata()); + $this->assertSame($entity, $context->getRoot()); + $this->assertSame($entity->reference, $context->getValue()); + $this->assertSame($entity->reference, $value); + + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $this->metadata->addConstraint(new Callback([ + 'callback' => $callback1, + 'groups' => 'Group', + ])); + $this->referenceMetadata->addConstraint(new Callback([ + 'callback' => $callback2, + 'groups' => 'Group', + ])); + + $violations = $this->validator->validate($entity, new Valid(), 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Message value', $violations[0]->getMessage()); + $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); + $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); + $this->assertSame('subpath[key]', $violations[0]->getPropertyPath()); + $this->assertSame($entity, $violations[0]->getRoot()); + $this->assertSame($entity->reference, $violations[0]->getInvalidValue()); + $this->assertNull($violations[0]->getPlural()); + $this->assertNull($violations[0]->getCode()); + } + + public function testTraverseTraversableByDefault() + { + $entity = new Entity(); + $traversable = new \ArrayIterator(['key' => $entity]); + + $callback = function ($value, ExecutionContextInterface $context) use ($entity, $traversable) { + $this->assertSame($this::ENTITY_CLASS, $context->getClassName()); + $this->assertNull($context->getPropertyName()); + $this->assertSame('[key]', $context->getPropertyPath()); + $this->assertSame('Group', $context->getGroup()); + $this->assertSame($this->metadata, $context->getMetadata()); + $this->assertSame($traversable, $context->getRoot()); + $this->assertSame($entity, $context->getValue()); + $this->assertSame($entity, $value); + + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $this->metadataFactory->addMetadata(new ClassMetadata('ArrayIterator')); + $this->metadata->addConstraint(new Callback([ + 'callback' => $callback, + 'groups' => 'Group', + ])); + + $violations = $this->validate($traversable, new Valid(), 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Message value', $violations[0]->getMessage()); + $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); + $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); + $this->assertSame('[key]', $violations[0]->getPropertyPath()); + $this->assertSame($traversable, $violations[0]->getRoot()); + $this->assertSame($entity, $violations[0]->getInvalidValue()); + $this->assertNull($violations[0]->getPlural()); + $this->assertNull($violations[0]->getCode()); + } + + public function testTraversalEnabledOnClass() + { + $entity = new Entity(); + $traversable = new \ArrayIterator(['key' => $entity]); + + $callback = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Message'); + }; + + $traversableMetadata = new ClassMetadata('ArrayIterator'); + $traversableMetadata->addConstraint(new Traverse(true)); + + $this->metadataFactory->addMetadata($traversableMetadata); + $this->metadata->addConstraint(new Callback([ + 'callback' => $callback, + 'groups' => 'Group', + ])); + + $violations = $this->validate($traversable, new Valid(), 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + } + + public function testTraversalDisabledOnClass() + { + $entity = new Entity(); + $traversable = new \ArrayIterator(['key' => $entity]); + + $callback = function ($value, ExecutionContextInterface $context) { + $this->fail('Should not be called'); + }; + + $traversableMetadata = new ClassMetadata('ArrayIterator'); + $traversableMetadata->addConstraint(new Traverse(false)); + + $this->metadataFactory->addMetadata($traversableMetadata); + $this->metadata->addConstraint(new Callback([ + 'callback' => $callback, + 'groups' => 'Group', + ])); + + $violations = $this->validate($traversable, new Valid(), 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(0, $violations); + } + + public function testExpectTraversableIfTraversalEnabledOnClass() + { + $this->expectException('Symfony\Component\Validator\Exception\ConstraintDefinitionException'); + $entity = new Entity(); + + $this->metadata->addConstraint(new Traverse(true)); + + $this->validator->validate($entity); + } + + public function testReferenceTraversalDisabledOnClass() + { + $entity = new Entity(); + $entity->reference = new \ArrayIterator(['key' => new Reference()]); + + $callback = function ($value, ExecutionContextInterface $context) { + $this->fail('Should not be called'); + }; + + $traversableMetadata = new ClassMetadata('ArrayIterator'); + $traversableMetadata->addConstraint(new Traverse(false)); + + $this->metadataFactory->addMetadata($traversableMetadata); + $this->referenceMetadata->addConstraint(new Callback([ + 'callback' => $callback, + 'groups' => 'Group', + ])); + $this->metadata->addPropertyConstraint('reference', new Valid()); + + $violations = $this->validate($entity, new Valid(), 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(0, $violations); + } + + public function testReferenceTraversalEnabledOnReferenceDisabledOnClass() + { + $entity = new Entity(); + $entity->reference = new \ArrayIterator(['key' => new Reference()]); + + $callback = function ($value, ExecutionContextInterface $context) { + $this->fail('Should not be called'); + }; + + $traversableMetadata = new ClassMetadata('ArrayIterator'); + $traversableMetadata->addConstraint(new Traverse(false)); + + $this->metadataFactory->addMetadata($traversableMetadata); + $this->referenceMetadata->addConstraint(new Callback([ + 'callback' => $callback, + 'groups' => 'Group', + ])); + $this->metadata->addPropertyConstraint('reference', new Valid([ + 'traverse' => true, + ])); + + $violations = $this->validate($entity, new Valid(), 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(0, $violations); + } + + public function testReferenceTraversalDisabledOnReferenceEnabledOnClass() + { + $entity = new Entity(); + $entity->reference = new \ArrayIterator(['key' => new Reference()]); + + $callback = function ($value, ExecutionContextInterface $context) { + $this->fail('Should not be called'); + }; + + $traversableMetadata = new ClassMetadata('ArrayIterator'); + $traversableMetadata->addConstraint(new Traverse(true)); + + $this->metadataFactory->addMetadata($traversableMetadata); + $this->referenceMetadata->addConstraint(new Callback([ + 'callback' => $callback, + 'groups' => 'Group', + ])); + $this->metadata->addPropertyConstraint('reference', new Valid([ + 'traverse' => false, + ])); + + $violations = $this->validate($entity, new Valid(), 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(0, $violations); + } + + public function testAddCustomizedViolation() + { + $entity = new Entity(); + + $callback = function ($value, ExecutionContextInterface $context) { + $context->buildViolation('Message %param%') + ->setParameter('%param%', 'value') + ->setInvalidValue('Invalid value') + ->setPlural(2) + ->setCode('42') + ->addViolation(); + }; + + $this->metadata->addConstraint(new Callback($callback)); + + $violations = $this->validator->validate($entity); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + $this->assertSame('Message value', $violations[0]->getMessage()); + $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); + $this->assertSame(['%param%' => 'value'], $violations[0]->getParameters()); + $this->assertSame('', $violations[0]->getPropertyPath()); + $this->assertSame($entity, $violations[0]->getRoot()); + $this->assertSame('Invalid value', $violations[0]->getInvalidValue()); + $this->assertSame(2, $violations[0]->getPlural()); + $this->assertSame('42', $violations[0]->getCode()); + } + + public function testNoDuplicateValidationIfClassConstraintInMultipleGroups() + { + $entity = new Entity(); + + $callback = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Message'); + }; + + $this->metadata->addConstraint(new Callback([ + 'callback' => $callback, + 'groups' => ['Group 1', 'Group 2'], + ])); + + $violations = $this->validator->validate($entity, new Valid(), ['Group 1', 'Group 2']); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + } + + public function testNoDuplicateValidationIfPropertyConstraintInMultipleGroups() + { + $entity = new Entity(); + + $callback = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Message'); + }; + + $this->metadata->addPropertyConstraint('firstName', new Callback([ + 'callback' => $callback, + 'groups' => ['Group 1', 'Group 2'], + ])); + + $violations = $this->validator->validate($entity, new Valid(), ['Group 1', 'Group 2']); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(1, $violations); + } + + public function testValidateFailsIfNoConstraintsAndNoObjectOrArray() + { + $this->expectException('Symfony\Component\Validator\Exception\RuntimeException'); + $this->validate('Foobar'); + } + + public function testAccessCurrentObject() + { + $called = false; + $entity = new Entity(); + $entity->firstName = 'Bernhard'; + $entity->data = ['firstName' => 'Bernhard']; + + $callback = function ($value, ExecutionContextInterface $context) use ($entity, &$called) { + $called = true; + $this->assertSame($entity, $context->getObject()); + }; + + $this->metadata->addConstraint(new Callback($callback)); + $this->metadata->addPropertyConstraint('firstName', new Callback($callback)); + $this->metadata->addPropertyConstraint('data', new Collection(['firstName' => new Expression('value == this.firstName')])); + + $this->validator->validate($entity); + + $this->assertTrue($called); + } + + public function testInitializeObjectsOnFirstValidation() + { + $entity = new Entity(); + $entity->initialized = false; + + // prepare initializers that set "initialized" to true + $initializer1 = $this->getMockBuilder('Symfony\\Component\\Validator\\ObjectInitializerInterface')->getMock(); + $initializer2 = $this->getMockBuilder('Symfony\\Component\\Validator\\ObjectInitializerInterface')->getMock(); + + $initializer1->expects($this->once()) + ->method('initialize') + ->with($entity) + ->willReturnCallback(function ($object) { + $object->initialized = true; + }); + + $initializer2->expects($this->once()) + ->method('initialize') + ->with($entity); + + $this->validator = $this->createValidator($this->metadataFactory, [ + $initializer1, + $initializer2, + ]); + + // prepare constraint which + // * checks that "initialized" is set to true + // * validates the object again + $callback = function ($object, ExecutionContextInterface $context) { + $this->assertTrue($object->initialized); + + // validate again in same group + $validator = $context->getValidator()->inContext($context); + + $validator->validate($object); + + // validate again in other group + $validator->validate($object, null, 'SomeGroup'); + }; + + $this->metadata->addConstraint(new Callback($callback)); + + $this->validate($entity); + + $this->assertTrue($entity->initialized); + } + + public function testPassConstraintToViolation() + { + $constraint = new FailingConstraint(); + $violations = $this->validate('Foobar', $constraint); + + $this->assertCount(1, $violations); + $this->assertSame($constraint, $violations[0]->getConstraint()); + } + + public function testCollectionConstraitViolationHasCorrectContext() + { + $data = [ + 'foo' => 'fooValue', + ]; + + // Missing field must not be the first in the collection validation + $constraint = new Collection([ + 'foo' => new NotNull(), + 'bar' => new NotNull(), + ]); + + $violations = $this->validate($data, $constraint); + + $this->assertCount(1, $violations); + $this->assertSame($constraint, $violations[0]->getConstraint()); + } + + public function testNestedObjectIsNotValidatedIfGroupInValidConstraintIsNotValidated() + { + $entity = new Entity(); + $entity->firstName = ''; + $reference = new Reference(); + $reference->value = ''; + $entity->childA = $reference; + + $this->metadata->addPropertyConstraint('firstName', new NotBlank(['groups' => 'group1'])); + $this->metadata->addPropertyConstraint('childA', new Valid(['groups' => 'group1'])); + $this->referenceMetadata->addPropertyConstraint('value', new NotBlank()); + + $violations = $this->validator->validate($entity, null, []); + + $this->assertCount(0, $violations); + } + + public function testNestedObjectIsValidatedIfGroupInValidConstraintIsValidated() + { + $entity = new Entity(); + $entity->firstName = ''; + $reference = new Reference(); + $reference->value = ''; + $entity->childA = $reference; + + $this->metadata->addPropertyConstraint('firstName', new NotBlank(['groups' => 'group1'])); + $this->metadata->addPropertyConstraint('childA', new Valid(['groups' => 'group1'])); + $this->referenceMetadata->addPropertyConstraint('value', new NotBlank(['groups' => 'group1'])); + + $violations = $this->validator->validate($entity, null, ['Default', 'group1']); + + $this->assertCount(2, $violations); + } + + public function testNestedObjectIsValidatedInMultipleGroupsIfGroupInValidConstraintIsValidated() + { + $entity = new Entity(); + $entity->firstName = null; + + $reference = new Reference(); + $reference->value = null; + + $entity->childA = $reference; + + $this->metadata->addPropertyConstraint('firstName', new NotBlank()); + $this->metadata->addPropertyConstraint('childA', new Valid(['groups' => ['group1', 'group2']])); + + $this->referenceMetadata->addPropertyConstraint('value', new NotBlank(['groups' => 'group1'])); + $this->referenceMetadata->addPropertyConstraint('value', new NotNull(['groups' => 'group2'])); + + $violations = $this->validator->validate($entity, null, ['Default', 'group1', 'group2']); + + $this->assertCount(3, $violations); + } + protected function createValidator(MetadataFactoryInterface $metadataFactory, array $objectInitializers = []): ValidatorInterface { $translator = new IdentityTranslator(); @@ -202,4 +2139,66 @@ public function testOptionalConstraintIsIgnored() $this->assertCount(0, $violations); } + + public function testValidatedConstraintsHashesDoNotCollide() + { + $metadata = new ClassMetadata(Entity::class); + $metadata->addPropertyConstraint('initialized', new NotNull(['groups' => 'should_pass'])); + $metadata->addPropertyConstraint('initialized', new IsNull(['groups' => 'should_fail'])); + + $this->metadataFactory->addMetadata($metadata); + + $entity = new Entity(); + $entity->data = new \stdClass(); + + $this->assertCount(2, $this->validator->validate($entity, new TestConstraintHashesDoNotCollide())); + } + + public function testValidatedConstraintsHashesDoNotCollideWithSameConstraintValidatingDifferentProperties() + { + $value = new \stdClass(); + + $entity = new Entity(); + $entity->firstName = $value; + $entity->setLastName($value); + + $validator = $this->validator->startContext($entity); + + $constraint = new IsNull(); + $validator->atPath('firstName') + ->validate($entity->firstName, $constraint); + $validator->atPath('lastName') + ->validate($entity->getLastName(), $constraint); + + $this->assertCount(2, $validator->getViolations()); + } +} + +final class TestConstraintHashesDoNotCollide extends Constraint +{ +} + +final class TestConstraintHashesDoNotCollideValidator extends ConstraintValidator +{ + /** + * {@inheritdoc} + */ + public function validate($value, Constraint $constraint) + { + if (!$value instanceof Entity) { + throw new \LogicException(); + } + + $this->context->getValidator() + ->inContext($this->context) + ->atPath('data') + ->validate($value, new NotNull()) + ->validate($value, new NotNull()) + ->validate($value, new IsFalse()); + + $this->context->getValidator() + ->inContext($this->context) + ->validate($value, null, new GroupSequence(['should_pass'])) + ->validate($value, null, new GroupSequence(['should_fail'])); + } } diff --git a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php index eaa1309fd0ff3..b1f1384ffbd16 100644 --- a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php +++ b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php @@ -108,7 +108,7 @@ public function validate($value, $constraints = null, $groups = null) $this->validateGenericNode( $value, $previousObject, - \is_object($value) ? spl_object_hash($value) : null, + \is_object($value) ? $this->generateCacheKey($value) : null, $metadata, $this->defaultPropertyPath, $groups, @@ -176,7 +176,7 @@ public function validateProperty($object, string $propertyName, $groups = null) $propertyMetadatas = $classMetadata->getPropertyMetadata($propertyName); $groups = $groups ? $this->normalizeGroups($groups) : $this->defaultGroups; - $cacheKey = spl_object_hash($object); + $cacheKey = $this->generateCacheKey($object); $propertyPath = PropertyPath::append($this->defaultPropertyPath, $propertyName); $previousValue = $this->context->getValue(); @@ -224,7 +224,7 @@ public function validatePropertyValue($objectOrClass, string $propertyName, $val if (\is_object($objectOrClass)) { $object = $objectOrClass; $class = \get_class($object); - $cacheKey = spl_object_hash($objectOrClass); + $cacheKey = $this->generateCacheKey($objectOrClass); $propertyPath = PropertyPath::append($this->defaultPropertyPath, $propertyName); } else { // $objectOrClass contains a class name @@ -311,7 +311,7 @@ private function validateObject($object, string $propertyPath, array $groups, in $this->validateClassNode( $object, - spl_object_hash($object), + $this->generateCacheKey($object), $classMetadata, $propertyPath, $groups, @@ -425,7 +425,7 @@ private function validateClassNode(object $object, ?string $cacheKey, ClassMetad $defaultOverridden = false; // Use the object hash for group sequences - $groupHash = \is_object($group) ? spl_object_hash($group) : $group; + $groupHash = \is_object($group) ? $this->generateCacheKey($group, true) : $group; if ($context->isGroupValidated($cacheKey, $groupHash)) { // Skip this group when validating the properties and when @@ -730,7 +730,7 @@ private function validateInGroup($value, ?string $cacheKey, MetadataInterface $m // Prevent duplicate validation of constraints, in the case // that constraints belong to multiple validated groups if (null !== $cacheKey) { - $constraintHash = spl_object_hash($constraint); + $constraintHash = $this->generateCacheKey($constraint, true); // instanceof Valid: In case of using a Valid constraint with many groups // it makes a reference object get validated by each group if ($constraint instanceof Composite || $constraint instanceof Valid) { @@ -762,4 +762,22 @@ private function validateInGroup($value, ?string $cacheKey, MetadataInterface $m } } } + + /** + * @param object $object + */ + private function generateCacheKey($object, bool $dependsOnPropertyPath = false): string + { + if ($this->context instanceof ExecutionContext) { + $cacheKey = $this->context->generateCacheKey($object); + } else { + $cacheKey = spl_object_hash($object); + } + + if ($dependsOnPropertyPath) { + $cacheKey .= $this->context->getPropertyPath(); + } + + return $cacheKey; + } } diff --git a/src/Symfony/Component/VarDumper/Caster/ArgsStub.php b/src/Symfony/Component/VarDumper/Caster/ArgsStub.php index 591c7e2a844a0..f8b485bd40c3f 100644 --- a/src/Symfony/Component/VarDumper/Caster/ArgsStub.php +++ b/src/Symfony/Component/VarDumper/Caster/ArgsStub.php @@ -24,7 +24,7 @@ class ArgsStub extends EnumStub public function __construct(array $args, string $function, ?string $class) { - list($variadic, $params) = self::getParameters($function, $class); + [$variadic, $params] = self::getParameters($function, $class); $values = []; foreach ($args as $k => $v) { diff --git a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php index 938bd03856227..f29dbac3a4df3 100644 --- a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php +++ b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php @@ -150,7 +150,10 @@ abstract class AbstractCloner implements ClonerInterface ':dba' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'], ':dba persistent' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'], + + 'GdImage' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castGd'], ':gd' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castGd'], + ':mysql link' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castMysqlLink'], ':pgsql large object' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLargeObject'], ':pgsql link' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLink'], @@ -158,9 +161,14 @@ abstract class AbstractCloner implements ClonerInterface ':pgsql result' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castResult'], ':process' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castProcess'], ':stream' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStream'], + + 'OpenSSLCertificate' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castOpensslX509'], ':OpenSSL X.509' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castOpensslX509'], + ':persistent stream' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStream'], ':stream-context' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStreamContext'], + + 'XmlParser' => ['Symfony\Component\VarDumper\Caster\XmlResourceCaster', 'castXml'], ':xml' => ['Symfony\Component\VarDumper\Caster\XmlResourceCaster', 'castXml'], 'RdKafka' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castRdKafka'], @@ -303,7 +311,7 @@ protected function castObject(Stub $stub, bool $isNested) $stub->class = get_debug_type($obj); } if (isset($this->classInfo[$class])) { - list($i, $parents, $hasDebugInfo, $fileInfo) = $this->classInfo[$class]; + [$i, $parents, $hasDebugInfo, $fileInfo] = $this->classInfo[$class]; } else { $i = 2; $parents = [$class]; diff --git a/src/Symfony/Component/VarDumper/Server/DumpServer.php b/src/Symfony/Component/VarDumper/Server/DumpServer.php index 3e6343e350258..28decf319185b 100644 --- a/src/Symfony/Component/VarDumper/Server/DumpServer.php +++ b/src/Symfony/Component/VarDumper/Server/DumpServer.php @@ -75,7 +75,7 @@ public function listen(callable $callback): void continue; } - list($data, $context) = $payload; + [$data, $context] = $payload; $callback($data, $context, $clientId); } diff --git a/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php index fcb1d146944a5..7eb60be917168 100644 --- a/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php +++ b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php @@ -338,7 +338,7 @@ public function unserialize($data) if ('' === $data) { throw new \InvalidArgumentException('Serialized data is empty.'); } - list(, $data) = unserialize($data); + [, $data] = unserialize($data); parent::unserialize($data); } } @@ -392,7 +392,7 @@ public function serialize(): string public function unserialize($str) { - list($this->foo) = unserialize($str); + [$this->foo] = unserialize($str); } } @@ -405,7 +405,7 @@ public function __serialize(): array public function __unserialize(array $data) { - list($this->foo) = $data; + [$this->foo] = $data; } public function __sleep(): array diff --git a/src/Symfony/Component/Workflow/Registry.php b/src/Symfony/Component/Workflow/Registry.php index 967db71c69258..3474e953fa637 100644 --- a/src/Symfony/Component/Workflow/Registry.php +++ b/src/Symfony/Component/Workflow/Registry.php @@ -29,7 +29,7 @@ public function addWorkflow(WorkflowInterface $workflow, WorkflowSupportStrategy public function has(object $subject, string $workflowName = null): bool { - foreach ($this->workflows as list($workflow, $supportStrategy)) { + foreach ($this->workflows as [$workflow, $supportStrategy]) { if ($this->supports($workflow, $supportStrategy, $subject, $workflowName)) { return true; } @@ -45,7 +45,7 @@ public function get(object $subject, string $workflowName = null) { $matched = []; - foreach ($this->workflows as list($workflow, $supportStrategy)) { + foreach ($this->workflows as [$workflow, $supportStrategy]) { if ($this->supports($workflow, $supportStrategy, $subject, $workflowName)) { $matched[] = $workflow; } @@ -72,7 +72,7 @@ public function get(object $subject, string $workflowName = null) public function all(object $subject): array { $matched = []; - foreach ($this->workflows as list($workflow, $supportStrategy)) { + foreach ($this->workflows as [$workflow, $supportStrategy]) { if ($supportStrategy->supports($workflow, $subject)) { $matched[] = $workflow; } diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 690c20298365d..f0ce2aaef047b 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -358,7 +358,7 @@ private function doParse(string $value, int $flags) } try { - return Inline::parse($this->parseQuotedString($this->currentLine), $flags, $this->refs); + return Inline::parse($this->lexInlineQuotedString(), $flags, $this->refs); } catch (ParseException $e) { $e->setParsedLine($this->getRealCurrentLineNb() + 1); $e->setSnippet($this->currentLine); @@ -371,7 +371,7 @@ private function doParse(string $value, int $flags) } try { - $parsedMapping = Inline::parse($this->lexInlineMapping($this->currentLine), $flags, $this->refs); + $parsedMapping = Inline::parse($this->lexInlineMapping(), $flags, $this->refs); while ($this->moveToNextLine()) { if (!$this->isCurrentLineEmpty()) { @@ -392,7 +392,7 @@ private function doParse(string $value, int $flags) } try { - $parsedSequence = Inline::parse($this->lexInlineSequence($this->currentLine), $flags, $this->refs); + $parsedSequence = Inline::parse($this->lexInlineSequence(), $flags, $this->refs); while ($this->moveToNextLine()) { if (!$this->isCurrentLineEmpty()) { @@ -667,6 +667,11 @@ private function getNextEmbedBlock(int $indentation = null, bool $inSequence = f return implode("\n", $data); } + private function hasMoreLines(): bool + { + return (\count($this->lines) - 1) > $this->currentLineNb; + } + /** * Moves the parser to the next line. */ @@ -744,59 +749,63 @@ private function parseValue(string $value, int $flags, string $context) try { if ('' !== $value && '{' === $value[0]) { - return Inline::parse($this->lexInlineMapping($value), $flags, $this->refs); + $cursor = \strlen($this->currentLine) - \strlen($value); + + return Inline::parse($this->lexInlineMapping($cursor), $flags, $this->refs); } elseif ('' !== $value && '[' === $value[0]) { - return Inline::parse($this->lexInlineSequence($value), $flags, $this->refs); + $cursor = \strlen($this->currentLine) - \strlen($value); + + return Inline::parse($this->lexInlineSequence($cursor), $flags, $this->refs); } - $quotation = '' !== $value && ('"' === $value[0] || "'" === $value[0]) ? $value[0] : null; + switch ($value[0] ?? '') { + case '"': + case "'": + $cursor = \strlen($this->currentLine) - \strlen($value); + $parsedValue = Inline::parse($this->lexInlineQuotedString($cursor), $flags, $this->refs); - // do not take following lines into account when the current line is a quoted single line value - if (null !== $quotation && self::preg_match('/^'.$quotation.'.*'.$quotation.'(\s*#.*)?$/', $value)) { - return Inline::parse($value, $flags, $this->refs); - } + if (isset($this->currentLine[$cursor]) && preg_replace('/\s*#.*$/A', '', substr($this->currentLine, $cursor))) { + throw new ParseException(sprintf('Unexpected characters near "%s".', substr($this->currentLine, $cursor))); + } - $lines = []; + return $parsedValue; + default: + $lines = []; - while ($this->moveToNextLine()) { - // unquoted strings end before the first unindented line - if (null === $quotation && 0 === $this->getCurrentLineIndentation()) { - $this->moveToPreviousLine(); + while ($this->moveToNextLine()) { + // unquoted strings end before the first unindented line + if (0 === $this->getCurrentLineIndentation()) { + $this->moveToPreviousLine(); - break; - } + break; + } - $lines[] = trim($this->currentLine); + $lines[] = trim($this->currentLine); + } - // quoted string values end with a line that is terminated with the quotation character - $escapedLine = str_replace(['\\\\', '\\"'], '', $this->currentLine); - if ('' !== $escapedLine && $escapedLine[-1] === $quotation) { - break; - } - } + for ($i = 0, $linesCount = \count($lines), $previousLineBlank = false; $i < $linesCount; ++$i) { + if ('' === $lines[$i]) { + $value .= "\n"; + $previousLineBlank = true; + } elseif ($previousLineBlank) { + $value .= $lines[$i]; + $previousLineBlank = false; + } else { + $value .= ' '.$lines[$i]; + $previousLineBlank = false; + } + } - for ($i = 0, $linesCount = \count($lines), $previousLineBlank = false; $i < $linesCount; ++$i) { - if ('' === $lines[$i]) { - $value .= "\n"; - $previousLineBlank = true; - } elseif ($previousLineBlank) { - $value .= $lines[$i]; - $previousLineBlank = false; - } else { - $value .= ' '.$lines[$i]; - $previousLineBlank = false; - } - } + Inline::$parsedLineNumber = $this->getRealCurrentLineNb(); - Inline::$parsedLineNumber = $this->getRealCurrentLineNb(); + $parsedValue = Inline::parse($value, $flags, $this->refs); - $parsedValue = Inline::parse($value, $flags, $this->refs); + if ('mapping' === $context && \is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue, ': ')) { + throw new ParseException('A colon cannot be used in an unquoted mapping value.', $this->getRealCurrentLineNb() + 1, $value, $this->filename); + } - if ('mapping' === $context && \is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue, ': ')) { - throw new ParseException('A colon cannot be used in an unquoted mapping value.', $this->getRealCurrentLineNb() + 1, $value, $this->filename); + return $parsedValue; } - - return $parsedValue; } catch (ParseException $e) { $e->setParsedLine($this->getRealCurrentLineNb() + 1); $e->setSnippet($this->currentLine); @@ -1145,128 +1154,153 @@ private function getLineTag(string $value, int $flags, bool $nextLineCheck = tru throw new ParseException(sprintf('Tags support is not enabled. You must use the flag "Yaml::PARSE_CUSTOM_TAGS" to use "%s".', $matches['tag']), $this->getRealCurrentLineNb() + 1, $value, $this->filename); } - private function parseQuotedString(string $yaml): ?string + private function lexInlineQuotedString(int &$cursor = 0): string { - if ('' === $yaml || ('"' !== $yaml[0] && "'" !== $yaml[0])) { - throw new \InvalidArgumentException(sprintf('"%s" is not a quoted string.', $yaml)); - } + $quotation = $this->currentLine[$cursor]; + $value = $quotation; + ++$cursor; - $lines = [$yaml]; + $previousLineWasNewline = true; + $previousLineWasTerminatedWithBackslash = false; + $lineNumber = 0; - while ($this->moveToNextLine()) { - $lines[] = $this->currentLine; - - if (!$this->isCurrentLineEmpty() && $yaml[0] === $this->currentLine[-1]) { - break; + do { + if (++$lineNumber > 1) { + $cursor += strspn($this->currentLine, ' ', $cursor); } - } - $value = ''; - - for ($i = 0, $linesCount = \count($lines), $previousLineWasNewline = false, $previousLineWasTerminatedWithBackslash = false; $i < $linesCount; ++$i) { - $trimmedLine = trim($lines[$i]); - if ('' === $trimmedLine) { + if ($this->isCurrentLineBlank()) { $value .= "\n"; } elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) { $value .= ' '; } - if ('' !== $trimmedLine && '\\' === $lines[$i][-1]) { - $value .= ltrim(substr($lines[$i], 0, -1)); - } elseif ('' !== $trimmedLine) { - $value .= $trimmedLine; + for (; \strlen($this->currentLine) > $cursor; ++$cursor) { + switch ($this->currentLine[$cursor]) { + case '\\': + if (isset($this->currentLine[++$cursor])) { + $value .= '\\'.$this->currentLine[$cursor]; + } + + break; + case $quotation: + ++$cursor; + + if ("'" === $quotation && isset($this->currentLine[$cursor]) && "'" === $this->currentLine[$cursor]) { + $value .= "''"; + break; + } + + return $value.$quotation; + default: + $value .= $this->currentLine[$cursor]; + } } - if ('' === $trimmedLine) { + if ($this->isCurrentLineBlank()) { $previousLineWasNewline = true; $previousLineWasTerminatedWithBackslash = false; - } elseif ('\\' === $lines[$i][-1]) { + } elseif ('\\' === $this->currentLine[-1]) { $previousLineWasNewline = false; $previousLineWasTerminatedWithBackslash = true; } else { $previousLineWasNewline = false; $previousLineWasTerminatedWithBackslash = false; } - } - return $value; - - for ($i = 1; isset($yaml[$i]) && $quotation !== $yaml[$i]; ++$i) { - } - - // quoted single line string - if (isset($yaml[$i]) && $quotation === $yaml[$i]) { - return $yaml; - } - - $lines = [$yaml]; - - while ($this->moveToNextLine()) { - for ($i = 1; isset($this->currentLine[$i]) && $quotation !== $this->currentLine[$i]; ++$i) { + if ($this->hasMoreLines()) { + $cursor = 0; } + } while ($this->moveToNextLine()); - $lines[] = trim($this->currentLine); - - if (isset($this->currentLine[$i]) && $quotation === $this->currentLine[$i]) { - break; - } - } + throw new ParseException('Malformed inline YAML string'); } - private function lexInlineMapping(string $yaml): string + private function lexUnquotedString(int &$cursor): string { - if ('' === $yaml || '{' !== $yaml[0]) { - throw new \InvalidArgumentException(sprintf('"%s" is not a sequence.', $yaml)); - } - - for ($i = 1; isset($yaml[$i]) && '}' !== $yaml[$i]; ++$i) { - } - - if (isset($yaml[$i]) && '}' === $yaml[$i]) { - return $yaml; - } + $offset = $cursor; + $cursor += strcspn($this->currentLine, '[]{},: ', $cursor); - $lines = [$yaml]; - - while ($this->moveToNextLine()) { - $lines[] = $this->currentLine; - } + return substr($this->currentLine, $offset, $cursor - $offset); + } - return implode("\n", $lines); + private function lexInlineMapping(int &$cursor = 0): string + { + return $this->lexInlineStructure($cursor, '}'); } - private function lexInlineSequence(string $yaml): string + private function lexInlineSequence(int &$cursor = 0): string { - if ('' === $yaml || '[' !== $yaml[0]) { - throw new \InvalidArgumentException(sprintf('"%s" is not a sequence.', $yaml)); - } + return $this->lexInlineStructure($cursor, ']'); + } - for ($i = 1; isset($yaml[$i]) && ']' !== $yaml[$i]; ++$i) { - } + private function lexInlineStructure(int &$cursor, string $closingTag): string + { + $value = $this->currentLine[$cursor]; + ++$cursor; - if (isset($yaml[$i]) && ']' === $yaml[$i]) { - return $yaml; - } + do { + $this->consumeWhitespaces($cursor); + + while (isset($this->currentLine[$cursor])) { + switch ($this->currentLine[$cursor]) { + case '"': + case "'": + $value .= $this->lexInlineQuotedString($cursor); + break; + case ':': + case ',': + $value .= $this->currentLine[$cursor]; + ++$cursor; + break; + case '{': + $value .= $this->lexInlineMapping($cursor); + break; + case '[': + $value .= $this->lexInlineSequence($cursor); + break; + case $closingTag: + $value .= $this->currentLine[$cursor]; + ++$cursor; + + return $value; + case '#': + break 2; + default: + $value .= $this->lexUnquotedString($cursor); + } - $value = $yaml; + if ($this->consumeWhitespaces($cursor)) { + $value .= ' '; + } + } - while ($this->moveToNextLine()) { - for ($i = 1; isset($this->currentLine[$i]) && ']' !== $this->currentLine[$i]; ++$i) { + if ($this->hasMoreLines()) { + $cursor = 0; } + } while ($this->moveToNextLine()); - $trimmedValue = trim($this->currentLine); + throw new ParseException('Malformed inline YAML string'); + } - if ('' !== $trimmedValue && '#' === $trimmedValue[0]) { - continue; - } + private function consumeWhitespaces(int &$cursor): bool + { + $whitespacesConsumed = 0; - $value .= $trimmedValue; + do { + $whitespaceOnlyTokenLength = strspn($this->currentLine, ' ', $cursor); + $whitespacesConsumed += $whitespaceOnlyTokenLength; + $cursor += $whitespaceOnlyTokenLength; - if (isset($this->currentLine[$i]) && ']' === $this->currentLine[$i]) { - break; + if (isset($this->currentLine[$cursor])) { + return 0 < $whitespacesConsumed; } - } - return $value; + if ($this->hasMoreLines()) { + $cursor = 0; + } + } while ($this->moveToNextLine()); + + return 0 < $whitespacesConsumed; } } diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index 6e8b997313251..c31646d512b31 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -1569,6 +1569,54 @@ public function testParseMultiLineUnquotedString() $this->assertSame(['foo' => 'bar baz foobar foo', 'bar' => 'baz'], $this->parser->parse($yaml)); } + /** + * @dataProvider escapedQuotationCharactersInQuotedStrings + */ + public function testParseQuotedStringContainingEscapedQuotationCharacters(string $yaml, array $expected) + { + $this->assertSame($expected, $this->parser->parse($yaml)); + } + + public function escapedQuotationCharactersInQuotedStrings() + { + return [ + 'single quoted string' => [ + << [ + [ + 'message' => 'No emails received before timeout - Address: \'test@testemail.company.com\' Keyword: \'Your Order confirmation\' ttl: 50', + 'outcome' => 'failed', + ], + ], + ], + ], + 'double quoted string' => [ + << [ + [ + 'message' => 'No emails received before timeout - Address: "test@testemail.company.com" Keyword: "Your Order confirmation" ttl: 50', + 'outcome' => 'failed', + ], + ], + ], + ], + ]; + } + public function testParseMultiLineString() { $this->assertEquals("foo bar\nbaz", $this->parser->parse("foo\nbar\n\nbaz")); @@ -1659,6 +1707,16 @@ public function inlineNotationSpanningMultipleLinesProvider(): array 'foo': 'bar', 'bar': 'baz' } +YAML + , + ], + 'mapping with unquoted strings and values' => [ + ['foo' => 'bar', 'bar' => 'baz'], + << [ + ['foo', 'bar'], + << [ + [ + 'foo' => [ + 'bar' => 'foobar', + ], + ], + << [ + [ + 'foo', + [ + 'bar', + 'baz', + ], + ], + << [ + [ + ['entry1', []], + ['entry2'], + ], + << [ ['foo' => ['bar', 'foobar'], 'bar' => ['baz']], << [ + [ + 'foobar' => [ + 'foo', + 'bar', + ], + 'bar' => 'baz', + ], + << [ [ 'foo' => [ @@ -1823,6 +1944,110 @@ public function inlineNotationSpanningMultipleLinesProvider(): array foo: 'bar baz' +YAML + ], + 'mixed mapping with inline notation having separated lines' => [ + [ + 'map' => [ + 'key' => 'value', + 'a' => 'b', + ], + 'param' => 'some', + ], + << [ + [ + 'map' => [ + 'key' => 'value', + 'a' => 'b', + ], + 'param' => 'some', + ], + << [ + [ + 'map' => [ + 'key' => 'value', + 'a' => 'b', + ], + 'param' => 'some', + ], + << [ + [ + [']'], + ['}'], + ['ba[r'], + ['[ba]r'], + ['bar]'], + ['foo' => 'bar{'], + ['foo' => 'b{ar}'], + ['foo' => 'bar}'], + ], + << [ + [ + ['te"st'], + ['test'], + ["te'st"], + ['te"st]'], + ['te"st'], + ['test'], + ["te'st"], + ['te"st]'], + ], + <<assertEquals(new TaggedValue('foo', ['foo' => 'bar']), $this->parser->parse('!foo {foo: bar}', Yaml::PARSE_CUSTOM_TAGS)); } + public function testInvalidInlineSequenceContainingStringWithEscapedQuotationCharacter() + { + $this->expectException(ParseException::class); + + $this->parser->parse('["\\"]'); + } + /** * @dataProvider taggedValuesProvider */