diff --git a/.composer/composer.json b/.composer/composer.json index 655ee13805f47..d69e9f7626d55 100644 --- a/.composer/composer.json +++ b/.composer/composer.json @@ -1,6 +1,5 @@ { "require": { - "php": ">=5.3.7", - "hirak/prestissimo": "^0.1.15" + "hirak/prestissimo": "^0.1.18" } } diff --git a/.composer/composer.lock b/.composer/composer.lock index 8e037ebb4af81..7027e21d9ac4b 100644 --- a/.composer/composer.lock +++ b/.composer/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "aa7aa2d143fd85800595242996021ada", - "content-hash": "51e9161b78dda1fe149a9e9c106be90b", + "hash": "e02685ff7d2409d34fa87e306fc86ec5", + "content-hash": "2854c05c76a78113c693dbbdd3f9c518", "packages": [ { "name": "hirak/prestissimo", @@ -65,8 +65,6 @@ "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, - "platform": { - "php": ">=5.3.7" - }, + "platform": [], "platform-dev": [] } diff --git a/.travis.yml b/.travis.yml index d82550017d9ab..4718a1090ac7b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,44 +35,46 @@ cache: services: mongodb before_install: + - PHP=$TRAVIS_PHP_VERSION # Matrix lines for intermediate PHP versions are skipped for pull requests - - if [[ ! $deps && ! $TRAVIS_PHP_VERSION = ${MIN_PHP%.*} && $TRAVIS_PHP_VERSION != hhvm && $TRAVIS_PULL_REQUEST != false ]]; then deps=skip; fi; + - if [[ ! $deps && ! $PHP = ${MIN_PHP%.*} && $PHP != hhvm && $TRAVIS_PULL_REQUEST != false ]]; then deps=skip; skip=1; fi # A sigchild-enabled-PHP is used to test the Process component on the lowest PHP matrix line - - if [[ ! $deps && $TRAVIS_PHP_VERSION = ${MIN_PHP%.*} && ! -d php-$MIN_PHP/sapi ]]; then wget http://museum.php.net/php5/php-$MIN_PHP.tar.bz2 -O - | tar -xj; (cd php-$MIN_PHP; ./configure --enable-sigchild --enable-pcntl; make -j2); fi; - - if [[ $TRAVIS_PHP_VERSION != hhvm ]]; then INI_FILE=~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; else INI_FILE=/etc/hhvm/php.ini; fi; - - echo memory_limit = -1 >> $INI_FILE - - echo session.gc_probability = 0 >> $INI_FILE - - if [[ $TRAVIS_PHP_VERSION = 5.* ]]; then echo extension = mongo.so >> $INI_FILE; fi; - - if [[ $TRAVIS_PHP_VERSION = 5.* ]]; then echo extension = memcache.so >> $INI_FILE; fi; - - if [[ $TRAVIS_PHP_VERSION = 5.* ]]; then (echo yes | pecl install -f apcu-4.0.10 && echo apc.enable_cli = 1 >> $INI_FILE); fi; - - if [[ $TRAVIS_PHP_VERSION = 7.* ]]; then (echo yes | pecl install -f apcu-5.1.2 && echo apc.enable_cli = 1 >> $INI_FILE); fi; - - if [[ $TRAVIS_PHP_VERSION = 5.* && ! $deps ]]; then (cd src/Symfony/Component/Debug/Resources/ext && phpize && ./configure && make && echo extension = $(pwd)/modules/symfony_debug.so >> $INI_FILE); fi; - - if [[ $TRAVIS_PHP_VERSION = 5.* ]]; then pecl install -f memcached-2.1.0; fi; - - if [[ $TRAVIS_PHP_VERSION != hhvm ]]; then echo extension = ldap.so >> $INI_FILE; fi; - - if [[ $TRAVIS_PHP_VERSION != hhvm ]]; then phpenv config-rm xdebug.ini; fi; - - if [[ $deps != skip ]]; then composer self-update; fi; - - if [[ $deps != skip && $TRAVIS_REPO_SLUG = symfony/symfony ]]; then cp .composer/* ~/.composer/; composer global install; fi; - - if [[ $deps != skip ]]; then ./phpunit install; fi; - - export PHPUNIT=$(readlink -f ./phpunit) + - if [[ ! $deps && $PHP = ${MIN_PHP%.*} && ! -d php-$MIN_PHP/sapi ]]; then wget http://museum.php.net/php5/php-$MIN_PHP.tar.bz2 -O - | tar -xj; (cd php-$MIN_PHP; ./configure --enable-sigchild --enable-pcntl; make -j2); fi + - if [[ $PHP != hhvm ]]; then INI_FILE=~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; else INI_FILE=/etc/hhvm/php.ini; fi + - if [[ ! $skip ]]; then echo memory_limit = -1 >> $INI_FILE; fi + - if [[ ! $skip ]]; then echo session.gc_probability = 0 >> $INI_FILE; fi + - if [[ ! $skip && $PHP = 5.* ]]; then echo extension = mongo.so >> $INI_FILE; fi + - if [[ ! $skip && $PHP = 5.* ]]; then echo extension = memcache.so >> $INI_FILE; fi + - if [[ ! $skip && $PHP = 5.* ]]; then (echo yes | pecl install -f apcu-4.0.10 && echo apc.enable_cli = 1 >> $INI_FILE); fi + - if [[ ! $skip && $PHP = 7.* ]]; then (echo yes | pecl install -f apcu-5.1.2 && echo apc.enable_cli = 1 >> $INI_FILE); fi + - if [[ ! $deps && $PHP = 5.* ]]; then (cd src/Symfony/Component/Debug/Resources/ext && phpize && ./configure && make && echo extension = $(pwd)/modules/symfony_debug.so >> $INI_FILE); fi + - if [[ ! $skip && $PHP = 5.* ]]; then pecl install -f memcached-2.1.0; fi + - if [[ ! $skip && $PHP != hhvm ]]; then echo extension = ldap.so >> $INI_FILE; fi + - if [[ ! $skip && $PHP != hhvm ]]; then phpenv config-rm xdebug.ini; fi + - if [[ ! $skip ]]; then composer self-update --stable; fi + - if [[ ! $skip ]]; then cp .composer/* ~/.composer/; composer global install; fi + - if [[ ! $skip ]]; then ./phpunit install; fi + - if [[ ! $skip && $deps ]]; then composer global remove hirak/prestissimo; fi + - if [[ ! $skip ]]; then export PHPUNIT=$(readlink -f ./phpunit); fi install: - - if [[ $deps != skip ]]; then COMPONENTS=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -printf '%h\n'); fi; + - if [[ ! $skip ]]; then COMPONENTS=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -printf '%h\n'); fi # Create local composer packages for each patched components and reference them in composer.json files when cross-testing components - - if [[ $deps != skip && $deps ]]; then php .travis.php $TRAVIS_COMMIT_RANGE $TRAVIS_BRANCH $COMPONENTS; fi; + - if [[ ! $skip && $deps ]]; then php .travis.php $TRAVIS_COMMIT_RANGE $TRAVIS_BRANCH $COMPONENTS; fi # For the master branch when deps=high, the version before master is checked out and tested with the locally patched components - - if [[ $deps = high && $TRAVIS_BRANCH = master ]]; then SYMFONY_VERSION=$(git ls-remote --heads | grep -o '/[1-9].*' | tail -n 1 | sed s/.//); else SYMFONY_VERSION=$(cat composer.json | grep '^ *"dev-master". *"[1-9]' | grep -o '[0-9.]*'); fi; - - if [[ $deps = high && $TRAVIS_BRANCH = master ]]; then git fetch origin $SYMFONY_VERSION; git checkout -m FETCH_HEAD; COMPONENTS=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -printf '%h\n'); ./phpunit install; fi; + - if [[ $deps = high && $TRAVIS_BRANCH = master ]]; then SYMFONY_VERSION=$(git ls-remote --heads | grep -o '/[1-9].*' | tail -n 1 | sed s/.//); else SYMFONY_VERSION=$(cat composer.json | grep '^ *"dev-master". *"[1-9]' | grep -o '[0-9.]*'); fi + - if [[ $deps = high && $TRAVIS_BRANCH = master ]]; then git fetch origin $SYMFONY_VERSION; git checkout -m FETCH_HEAD; COMPONENTS=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -printf '%h\n'); ./phpunit install; fi # Legacy tests are skipped when deps=high and when the current branch version has not the same major version number than the next one - - if [[ $deps = high && ${SYMFONY_VERSION%.*} != $(git show $(git ls-remote --heads | grep -FA1 /$SYMFONY_VERSION | tail -n 1):composer.json | grep '^ *"dev-master". *"[1-9]' | grep -o '[0-9]*' | head -n 1) ]]; then LEGACY=,legacy; fi; - - export COMPOSER_ROOT_VERSION=$SYMFONY_VERSION.x-dev; - - if [[ ! $deps ]]; then composer update; else export SYMFONY_DEPRECATIONS_HELPER=weak; fi; - - if [[ $TRAVIS_BRANCH = master ]]; then export SYMFONY_PHPUNIT_OVERLOAD=1; fi; - - if [[ $TRAVIS_PHP_VERSION != hhvm ]]; then php -i; else hhvm --php -r 'print_r($_SERVER);print_r(ini_get_all());'; fi; + - if [[ $deps = high && ${SYMFONY_VERSION%.*} != $(git show $(git ls-remote --heads | grep -FA1 /$SYMFONY_VERSION | tail -n 1):composer.json | grep '^ *"dev-master". *"[1-9]' | grep -o '[0-9]*' | head -n 1) ]]; then LEGACY=,legacy; fi + - export COMPOSER_ROOT_VERSION=$SYMFONY_VERSION.x-dev + - if [[ ! $deps ]]; then composer update; else export SYMFONY_DEPRECATIONS_HELPER=weak; fi + - if [[ $TRAVIS_BRANCH = master ]]; then export SYMFONY_PHPUNIT_OVERLOAD=1; fi + - if [[ $PHP != hhvm ]]; then php -i; else hhvm --php -r 'print_r($_SERVER);print_r(ini_get_all());'; fi script: - - if [[ ! $deps ]]; then echo "$COMPONENTS" | parallel --gnu '$PHPUNIT --exclude-group tty,benchmark,intl-data {}'; fi; - - if [[ ! $deps ]]; then echo -e "\\nRunning tests requiring tty"; $PHPUNIT --group tty; fi; - - if [[ ! $deps && $TRAVIS_PHP_VERSION = ${MIN_PHP%.*} ]]; then echo -e "1\\n0" | xargs -I{} sh -c 'echo "\\nPHP --enable-sigchild enhanced={}" && ENHANCE_SIGCHLD={} php-$MIN_PHP/sapi/cli/php .phpunit/phpunit-4.8/phpunit --colors=always src/Symfony/Component/Process/'; fi; - - if [[ $deps = high ]]; then echo "$COMPONENTS" | parallel --gnu -j10% 'cd {}; composer update; $PHPUNIT --exclude-group tty,benchmark,intl-data'$LEGACY; fi; - - if [[ $deps = low ]]; then echo "$COMPONENTS" | parallel --gnu -j10% 'cd {}; composer update --prefer-lowest --prefer-stable; $PHPUNIT --exclude-group tty,benchmark,intl-data'; fi; - - if [[ $deps = skip ]]; then echo This matrix line is skipped for pull requests.; fi; + - if [[ $skip ]]; then echo -e "\\n\\e[1;34mIntermediate PHP version $PHP is skipped for pull requests.\\e[0m"; fi + - if [[ ! $deps ]]; then echo "$COMPONENTS" | parallel --gnu '$PHPUNIT --exclude-group tty,benchmark,intl-data {}'; fi + - if [[ ! $deps ]]; then echo -e "\\nRunning tests requiring tty"; $PHPUNIT --group tty; fi + - if [[ ! $deps && $PHP = ${MIN_PHP%.*} ]]; then echo -e "1\\n0" | xargs -I{} sh -c 'echo "\\nPHP --enable-sigchild enhanced={}" && ENHANCE_SIGCHLD={} php-$MIN_PHP/sapi/cli/php .phpunit/phpunit-4.8/phpunit --colors=always src/Symfony/Component/Process/'; fi + - if [[ $deps = high ]]; then echo "$COMPONENTS" | parallel --gnu -j10% 'cd {}; composer update --no-progress --ansi; $PHPUNIT --exclude-group tty,benchmark,intl-data'$LEGACY; fi + - if [[ $deps = low ]]; then echo "$COMPONENTS" | parallel --gnu -j10% 'cd {}; composer update --no-progress --ansi --prefer-lowest --prefer-stable; $PHPUNIT --exclude-group tty,benchmark,intl-data'; fi diff --git a/CHANGELOG-2.7.md b/CHANGELOG-2.7.md index 5b1d9382488ff..89ee3b549f479 100644 --- a/CHANGELOG-2.7.md +++ b/CHANGELOG-2.7.md @@ -7,6 +7,37 @@ in 2.7 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/v2.7.0...v2.7.1 +* 2.7.12 (2016-04-29) + + * bug #18180 [Form] fixed BC break with pre selection of choices with `ChoiceType` and its children (HeahDude) + * bug #18562 [WebProfilerBunde] Give an absolute url in case the request occured from another domain (romainneutron) + * bug #18603 [PropertyAccess] ->getValue() should be read-only (nicolas-grekas) + * bug #18593 [VarDumper] Fix dumping type hints for non-existing parent classes (nicolas-grekas) + * bug #18581 [Console] [TableHelper] make it work with SymfonyStyle. (aitboudad) + * bug #18280 [Routing] add query param if value is different from default (Tobion) + * bug #18496 [Console] use ANSI escape sequences in ProgressBar overwrite method (alekitto) + * bug #18491 [DependencyInjection] anonymous services are always private (xabbuh) + * bug #18515 [Filesystem] Better error handling in remove() (nicolas-grekas) + * bug #18449 [PropertyAccess] Fix regression (nicolas-grekas) + * bug #18429 [Console] Correct time formatting. (camporter) + * bug #18467 [DependencyInjection] Resolve aliases before removing abstract services + add tests (nicolas-grekas) + * bug #18460 [DomCrawler] Fix select option with empty value (Matt Wells) + * bug #18425 [Security] Fixed SwitchUserListener when exiting an impersonation with AnonymousToken (lyrixx) + * bug #18317 [Form] fix "prototype" not required when parent form is not required (HeahDude) + * bug #18439 [Logging] Add support for Firefox (43+) in ChromePhpHandler (arjenm) + * bug #18385 Detect CLI color support for Windows 10 build 10586 (mlocati) + * bug #18426 [EventDispatcher] Try first if the event is Stopped (lyrixx) + * bug #18394 [FrameworkBundle] Return the invokable service if its name is the class name (dunglas) + * bug #18265 Optimize ReplaceAliasByActualDefinitionPass (ajb-in) + * bug #18349 [Process] Fix stream_select priority when writing to stdin (nicolas-grekas) + * bug #18358 [Form] NumberToLocalizedStringTransformer should return floats when possible (nicolas-grekas) + * bug #17926 [DependencyInjection] Enable alias for service_container (hason) + * bug #18352 [Debug] Fix case sensitivity checks (nicolas-grekas) + * bug #18336 [Debug] Fix handling of php7 throwables (nicolas-grekas) + * bug #18354 [FrameworkBundle][TwigBridge] fix high deps tests (xabbuh) + * bug #18312 [ClassLoader] Fix storing not-found classes in APC cache (nicolas-grekas) + * bug #18298 [Validator] do not treat payload as callback (xabbuh) + * 2.7.11 (2016-03-25) * bug #18255 [HttpFoundation] Fix support of custom mime types with parameters (Ener-Getick) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 79ca50e61c6f6..456aeba37aa74 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -9,25 +9,25 @@ Symfony is the result of the work of many people who made the code better - Bernhard Schussek (bschussek) - Tobias Schultze (tobion) - Christophe Coevoet (stof) + - Christian Flothmann (xabbuh) - Jordi Boggiano (seldaek) - Victor Berchet (victor) - - Christian Flothmann (xabbuh) - Johannes S (johannes) - Kris Wallsmith (kriswallsmith) - Jakub Zalas (jakubzalas) - Ryan Weaver (weaverryan) - Javier Eguiluz (javier.eguiluz) - Hugo Hamon (hhamon) + - Kévin Dunglas (dunglas) - Abdellatif Ait boudad (aitboudad) - Pascal Borreli (pborreli) - - Kévin Dunglas (dunglas) - Joseph Bielawski (stloyd) - Wouter De Jong (wouterj) - Karma Dordrak (drak) - Romain Neutron (romain) - Lukas Kahwe Smith (lsmith) - - Jeremy Mikola (jmikola) - Martin Hasoň (hason) + - Jeremy Mikola (jmikola) - Jean-François Simon (jfsimon) - Benjamin Eberlei (beberlei) - Igor Wiedler (igorw) @@ -42,6 +42,7 @@ Symfony is the result of the work of many people who made the code better - ornicar - stealth35 ‏ (stealth35) - Alexander Mols (asm89) + - Jules Pietri (heah) - Francis Besset (francisbesset) - Bulat Shakirzyanov (avalanche123) - Saša Stamenković (umpirsky) @@ -56,13 +57,15 @@ Symfony is the result of the work of many people who made the code better - Michel Weimerskirch (mweimerskirch) - Eric Clemmons (ericclemmons) - Andrej Hudec (pulzarraider) + - Peter Rehm (rpet) - Christian Raue - Matthias Pigulla (mpdude) - - Peter Rehm (rpet) - Deni - Henrik Westphal (snc) - Dariusz Górecki (canni) - Arnout Boks (aboks) + - Iltar van der Berg (kjarli) + - Ener-Getick (energetick) - Douglas Greenshields (shieldo) - Lee McDermott - Brandon Turner @@ -71,21 +74,18 @@ Symfony is the result of the work of many people who made the code better - Pierre du Plessis (pierredup) - Bart van den Burg (burgov) - Jordan Alliot (jalliot) + - Charles Sarrazin (csarrazi) - John Wards (johnwards) - Toni Uebernickel (havvg) - Fran Moreno (franmomu) - Graham Campbell (graham) - Antoine Hérault (herzult) - - Iltar van der Berg (kjarli) - Arnaud Le Blanc (arnaud-lb) - Jérôme Tamarelle (gromnan) - - Jules Pietri (heah) - Michal Piotrowski (eventhorizon) - Tim Nagel (merk) - Paráda József (paradajozsef) - Brice BERNARD (brikou) - - Ener-Getick (energetick) - - Charles Sarrazin (csarrazi) - Alexander M. Turek (derrabus) - Dariusz Ruminski - marc.weistroff @@ -93,6 +93,7 @@ Symfony is the result of the work of many people who made the code better - Włodzimierz Gajda (gajdaw) - Alexander Schwenn (xelaris) - Florian Voutzinos (florianv) + - Konstantin Myakshin (koc) - Colin Frei - Adrien Brault (adrienbrault) - Joshua Thijssen @@ -100,19 +101,18 @@ Symfony is the result of the work of many people who made the code better - Peter Kokot (maastermedia) - excelwebzone - Jacob Dreesen (jdreesen) - - Konstantin Myakshin (koc) - Jérémy DERUSSÉ (jderusse) - Vladimir Reznichenko (kalessil) - Baptiste Clavié (talus) - Fabien Pennequin (fabienpennequin) - Gordon Franke (gimler) + - David Buchmann (dbu) - Tomáš Votruba (tomas_votruba) - Jáchym Toušek - Robert Schönthal (digitalkaoz) - Florian Lonqueu-Brochard (florianlb) - Eric GELOEN (gelo) - Stefano Sala (stefano.sala) - - David Buchmann (dbu) - Juti Noppornpitak (shiroyuki) - Tigran Azatyan (tigranazatyan) - Sebastian Hörl (blogsh) @@ -127,26 +127,27 @@ Symfony is the result of the work of many people who made the code better - Andréia Bohner (andreia) - Rafael Dohms (rdohms) - Arnaud Kleinpeter (nanocom) + - Joel Wurtz (brouznouf) - Philipp Wahala (hifi) - Richard Shank (iampersistent) - Thomas Rabaix (rande) - Vincent AUBERT (vincent) - - Joel Wurtz (brouznouf) - Mikael Pajunen - Clemens Tolboom - Helmer Aaviksoo - Hiromi Hishida (77web) + - Richard van Laak (rvanlaak) - Matthieu Ouellette-Vachon (maoueh) - Michał Pipa (michal.pipa) - Amal Raghav (kertz) - Jonathan Ingram (jonathaningram) + - Titouan Galopin (tgalopin) - Artur Kotyrba - Rouven Weßling (realityking) - Warnar Boekkooi (boekkooi) - Dmitrii Chekaliuk (lazyhammer) - Clément JOBEILI (dator) - Daniel Wehner - - Richard van Laak (rvanlaak) - Possum - Dorian Villet (gnutix) - Javier Spagnoletti (phansys) @@ -183,7 +184,6 @@ Symfony is the result of the work of many people who made the code better - Sergey Linnik (linniksa) - Michaël Perrin (michael.perrin) - Marcel Beerta (mazen) - - Titouan Galopin (tgalopin) - Loïc Faugeron - Jannik Zschiesche (apfelbox) - Marco Pivetta (ocramius) @@ -209,6 +209,7 @@ Symfony is the result of the work of many people who made the code better - Katsuhiro OGAWA - Alif Rachmawadi - Kristen Gilden (kgilden) + - Dawid Nowak - Pierre-Yves LEBECQ (pylebecq) - Jakub Kucharovic (jkucharovic) - Eugene Leonovich (rybakit) @@ -226,6 +227,7 @@ Symfony is the result of the work of many people who made the code better - Nikita Konstantinov - Wodor Wodorski - Thomas Lallement (raziel057) + - Matthieu Napoli (mnapoli) - Beau Simensen (simensen) - Michael Hirschler (mvhirsch) - Robert Kiss (kepten) @@ -237,7 +239,7 @@ Symfony is the result of the work of many people who made the code better - Peter Kruithof (pkruithof) - Michael Holm (hollo) - Marc Weistroff (futurecat) - - Dawid Nowak + - Hidde Wieringa (hiddewie) - Chris Smith (cs278) - Florian Klein (docteurklein) - Manuel Kiessling (manuelkiessling) @@ -272,12 +274,12 @@ Symfony is the result of the work of many people who made the code better - janschoenherr - Thomas Schulz (king2500) - Berny Cantos (xphere81) + - Teoh Han Hui (teohhanhui) - Ricard Clau (ricardclau) - Mark Challoner (markchalloner) - Gregor Harlan (gharlan) - Gennady Telegin (gtelegin) - Giorgio Premi - - Matthieu Napoli (mnapoli) - Ben Davies (bendavies) - Erin Millard - Artur Melo (restless) @@ -296,6 +298,8 @@ Symfony is the result of the work of many people who made the code better - Yaroslav Kiliba - Terje Bråten - Robbert Klarenbeek (robbertkl) + - Alessandro Chitolina + - JhonnyL - hossein zolfi (ocean) - Clément Gautier (clementgautier) - Eduardo Gulias (egulias) @@ -321,6 +325,7 @@ Symfony is the result of the work of many people who made the code better - Kai - Lee Rowlands - Maximilian Reichel (phramz) + - Loick Piera (pyrech) - Karoly Negyesi (chx) - Ivan Kurnosov - Xavier HAUSHERR @@ -351,6 +356,7 @@ Symfony is the result of the work of many people who made the code better - Niklas Fiekas - Markus Bachmann (baachi) - lancergr + - Mihai Stancu - Olivier Dolbeau (odolbeau) - Jan Rosier (rosier) - vagrant @@ -367,7 +373,6 @@ Symfony is the result of the work of many people who made the code better - cedric lombardot (cedriclombardot) - Jonas Flodén (flojon) - Christian Schmidt - - Hidde Wieringa (hiddewie) - Marek Štípek (maryo) - Marcin Sikoń (marphi) - Dominik Zogg (dominik.zogg) @@ -380,7 +385,6 @@ Symfony is the result of the work of many people who made the code better - Zander Baldwin - Adam Harvey - Alex Bakhturin - - Alessandro Chitolina - boombatower - Fabrice Bernhard (fabriceb) - Jérôme Macias (jeromemacias) @@ -425,12 +429,13 @@ Symfony is the result of the work of many people who made the code better - Norbert Orzechowicz (norzechowicz) - Denis Charrier (brucewouaigne) - Matthijs van den Bos (matthijs) - - Loick Piera (pyrech) - Lenard Palko - Nils Adermann (naderman) - Gábor Fási - DUPUCH (bdupuch) - Benjamin Leveque (benji07) + - Nate (frickenate) + - jhonnyL - sasezaki - Dawid Pakuła (zulusx) - Florian Rey (nervo) @@ -453,7 +458,9 @@ Symfony is the result of the work of many people who made the code better - Marcin Chyłek (songoq) - Ned Schwartz - Ziumin + - Jeremy Benoist - Lenar Lõhmus + - Krzysztof Piasecki (krzysztek) - Benjamin Laugueux (yzalis) - Zach Badgett (zachbadgett) - Aurélien Fredouelle @@ -492,12 +499,13 @@ Symfony is the result of the work of many people who made the code better - Javier López (loalf) - Reinier Kip - Dustin Dobervich (dustin10) + - Anne-Sophie Bachelard (annesophie) - Sebastian Marek (proofek) - Erkhembayar Gantulga (erheme318) - Michal Trojanowski - - Mihai Stancu - David Fuhr - Kamil Kokot (pamil) + - Aurimas Niekis (gcds) - Max Grigorian (maxakawizard) - Rostyslav Kinash - Maciej Malarz (malarzm) @@ -521,11 +529,13 @@ Symfony is the result of the work of many people who made the code better - Arturs Vonda - Sascha Grossenbacher - Szijarto Tamas + - Catalin Dan - Stephan Vock - Benjamin Zikarsky (bzikarsky) - Simon Schick (simonsimcity) - redstar504 - Tristan Roussel + - Cameron Porter - Hossein Bukhamsin - Disparity - origaminal @@ -579,6 +589,7 @@ Symfony is the result of the work of many people who made the code better - Romain Gautier (mykiwi) - Yosmany Garcia (yosmanyga) - Wouter de Wild + - Miroslav Sustek - Degory Valentine - Benoit Lévêque (benoit_leveque) - Jeroen Fiege (fieg) @@ -587,6 +598,8 @@ Symfony is the result of the work of many people who made the code better - possum - Denis Zunke (donalberto) - Olivier Maisonneuve (olineuve) + - Michele Locati + - Masterklavi - Francis Turmel (fturmel) - cgonzalez - Ben @@ -598,6 +611,7 @@ Symfony is the result of the work of many people who made the code better - Adrien Lucas (adrienlucas) - James Michael DuPont - Tom Klingenberg + - Jhonny Lidfors (jhonne) - Christopher Hall (mythmakr) - Paul Kamer (pkamer) - Rafał Wrzeszcz (rafalwrzeszcz) @@ -607,6 +621,7 @@ Symfony is the result of the work of many people who made the code better - Pierre Vanliefland (pvanliefland) - Sofiane HADDAG (sofhad) - frost-nzcr4 + - Arjen van der Meijden - Abhoryo - Fabian Vogler (fabian) - Korvin Szanto @@ -614,6 +629,7 @@ Symfony is the result of the work of many people who made the code better - Maksim Kotlyar (makasim) - Neil Ferreira - Dmitry Parnas (parnas) + - Théo FIDRY (theofidry) - Paul LE CORRE - DQNEO - Emanuele Iannone @@ -670,10 +686,8 @@ Symfony is the result of the work of many people who made the code better - omerida - Gábor Tóth - Daniel Cestari - - Jeremy Benoist - David Lima - Jérôme Vasseur - - Krzysztof Piasecki (krzysztek) - Brunet Laurent (lbrunet) - Magnus Nordlander (magnusnordlander) - Mikhail Yurasov (mym) @@ -715,7 +729,6 @@ Symfony is the result of the work of many people who made the code better - fabios - Sander Coolen (scoolen) - Nicolas Le Goff (nlegoff) - - Anne-Sophie Bachelard (annesophie) - Manuele Menozzi - Anton Babenko (antonbabenko) - Irmantas Šiupšinskas (irmantas) @@ -723,6 +736,7 @@ Symfony is the result of the work of many people who made the code better - Zachary Tong (polyfractal) - Hryhorii Hrebiniuk - mcfedr (mcfedr) + - hamza - dantleech - Xavier Leune - Tero Alén (tero) @@ -735,6 +749,7 @@ Symfony is the result of the work of many people who made the code better - Sortex - chispita - Wojciech Sznapka + - Ariel J. Birnbaum - Arjan Keeman - Máximo Cuadros (mcuadros) - tamirvs @@ -750,7 +765,6 @@ Symfony is the result of the work of many people who made the code better - Ville Mattila - Boris Vujicic (boris.vujicic) - Max Beutel - - Catalin Dan - nacho - Piotr Antosik (antek88) - Artem Lopata @@ -783,6 +797,7 @@ Symfony is the result of the work of many people who made the code better - Benoit Garret - Thomas Royer (cydonia7) - DerManoMann + - Jhonny Lidfors (jhonny) - Julien Bianchi (jubianchi) - Marcin Chwedziak - Roland Franssen (ro0) @@ -796,7 +811,6 @@ Symfony is the result of the work of many people who made the code better - rpg600 - Péter Buri (burci) - Davide Borsatto (davide.borsatto) - - Teoh Han Hui (teohhanhui) - kaiwa - Charles Sanquer (csanquer) - Albert Ganiev (helios-ag) @@ -899,6 +913,7 @@ Symfony is the result of the work of many people who made the code better - Berat Doğan - Anthony Ferrara - Klaas Cuvelier (kcuvelier) + - Steve Frécinaux - ShiraNai7 - Vašek Purchart (vasek-purchart) - Janusz Jabłoński (yanoosh) @@ -919,7 +934,6 @@ Symfony is the result of the work of many people who made the code better - Pete Mitchell (peterjmit) - Tom Corrigan (tomcorrigan) - Martin Pärtel - - Miroslav Sustek - Patrick Daley (padrig) - Xavier Briand (xavierbriand) - Max Summe @@ -943,7 +957,6 @@ Symfony is the result of the work of many people who made the code better - Nathaniel Catchpole - Jose Gonzalez - Adrien Samson (adriensamson) - - Aurimas Niekis (gcds) - Samuel Gordalina (gordalina) - Max Romanovsky (maxromanovsky) - Mathieu Morlon @@ -960,6 +973,7 @@ Symfony is the result of the work of many people who made the code better - r1pp3rj4ck - Robert Queck - mlively + - Amine Matmati - Fabian Steiner (fabstei) - Klaus Silveira (klaussilveira) - Thomas Chmielowiec (chmielot) @@ -984,6 +998,7 @@ Symfony is the result of the work of many people who made the code better - Benjamin Bender - Konrad Mohrfeldt - Lance Chen + - Andrey Astakhov (aast) - kor3k kor3k (kor3k) - Stelian Mocanita (stelian) - Flavian (2much) @@ -1010,6 +1025,7 @@ Symfony is the result of the work of many people who made the code better - Jakub Simon - Bouke Haarsma - Martin Eckhardt + - natechicago - Jonathan Poston - Adrian Olek (adrianolek) - Przemysław Piechota (kibao) @@ -1032,12 +1048,13 @@ Symfony is the result of the work of many people who made the code better - Josef Cech - Arnau González (arnaugm) - Simon Bouland (bouland) - - Nate (frickenate) - Matthew Foster (mfoster) - Paul Seiffert (seiffert) - Vasily Khayrulin (sirian) - Stefan Koopmanschap (skoop) - Stefan Hüsges (tronsha) + - Dan Blows + - Matt Wells - stloyd - Chris Tickner - Andrew Coulton @@ -1051,6 +1068,7 @@ Symfony is the result of the work of many people who made the code better - Andreas - Thomas Chmielowiec - Andrey Ryaguzov + - Peter Bex - Manatsawin Hanmongkolchai - Gunther Konig - Maciej Schmidt @@ -1081,10 +1099,12 @@ Symfony is the result of the work of many people who made the code better - Aarón Nieves Fernández - Mike Meier - Kirill Saksin + - Koalabaerchen - michalmarcinkowski - Warwick - Chris - JakeFr + - Simon Sargeant - efeen - Michał Dąbrowski (defrag) - Nathanael Noblet (gnat) @@ -1118,10 +1138,12 @@ Symfony is the result of the work of many people who made the code better - Jason Woods - dened - Dmitry Korotovsky + - Michael van Tricht - Sam Ward - Walther Lalk - Adam - devel + - taiiiraaa - Trevor Suarez - gedrox - dropfen @@ -1229,6 +1251,7 @@ Symfony is the result of the work of many people who made the code better - Muriel (metalmumu) - Michael Pohlers (mick_the_big) - Cayetano Soriano Gallego (neoshadybeat) + - Patrick McDougle (patrick-mcdougle) - Pablo Monterde Perez (plebs) - Jimmy Leger (redpanda) - Cyrille Jouineau (tuxosaurus) @@ -1269,6 +1292,7 @@ Symfony is the result of the work of many people who made the code better - Myke79 - Brian Debuire - Piers Warmers + - Guilliam Xavier - Sylvain Lorinet - klyk50 - Andreas Lutro @@ -1332,6 +1356,7 @@ Symfony is the result of the work of many people who made the code better - Norman Soetbeer - zorn - Benjamin Long + - Robin Chalas - Matt Janssen - Peter Gribanov - kwiateusz @@ -1382,6 +1407,7 @@ Symfony is the result of the work of many people who made the code better - Adel ELHAIBA (eadel) - Damián Nohales (eagleoneraptor) - Elliot Anderson (elliot) + - Sergey Zolotov (enleur) - Fabien D. (fabd) - Sorin Gitlan (forapathy) - Yohan Giarelli (frequence-web) @@ -1395,6 +1421,7 @@ Symfony is the result of the work of many people who made the code better - Jose Manuel Gonzalez (jgonzalez) - Jorge Maiden (jorgemaiden) - Justin Rainbow (jrainbow) + - Juan Luis (juanlugb) - JuntaTom (juntatom) - Ismail Faizi (kanafghan) - Sébastien Armand (khepin) @@ -1439,6 +1466,7 @@ Symfony is the result of the work of many people who made the code better - Víctor Mateo (victormateo) - Vincent (vincent1870) - Eugene Babushkin (warl) + - Wouter Sioen (wouter_sioen) - Xavier Amado (xamado) - Jesper Søndergaard Pedersen (zerrvox) - Florent Cailhol @@ -1451,6 +1479,8 @@ Symfony is the result of the work of many people who made the code better - Andreas Streichardt - smokeybear87 - Gustavo Adrian + - Kevin Weber + - Sergey Fedotov - Michael - fh-github@fholzhauer.de - Mark Topper diff --git a/appveyor.yml b/appveyor.yml index 7090d489a5a5f..2de9fb45d92ad 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,11 +21,11 @@ install: - IF %PHP%==1 appveyor DownloadFile https://curl.haxx.se/ca/cacert.pem - IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/releases/archives/php-5.3.11-nts-Win32-VC9-x86.zip - IF %PHP%==1 7z x php-5.3.11-nts-Win32-VC9-x86.zip -y >nul - - IF %PHP%==1 appveyor DownloadFile http://nebm.ist.utl.pt/~glopes/misc/intl_win/ICU-51.2-dlls.zip + - IF %PHP%==1 appveyor DownloadFile https://raw.githubusercontent.com/symfony/binary-utils/master/ICU-51.2-dlls.zip - IF %PHP%==1 7z x ICU-51.2-dlls.zip -y >nul - IF %PHP%==1 del /Q *.zip - IF %PHP%==1 cd ext - - IF %PHP%==1 appveyor DownloadFile http://nebm.ist.utl.pt/~glopes/misc/intl_win/php_intl-3.0.0-5.3-nts-vc9-x86.zip + - IF %PHP%==1 appveyor DownloadFile https://raw.githubusercontent.com/symfony/binary-utils/master/php_intl-3.0.0-5.3-nts-vc9-x86.zip - IF %PHP%==1 7z x php_intl-3.0.0-5.3-nts-vc9-x86.zip -y >nul - IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/pecl/releases/apcu/4.0.10/php_apcu-4.0.10-5.3-nts-vc9-x86.zip - IF %PHP%==1 7z x php_apcu-4.0.10-5.3-nts-vc9-x86.zip -y >nul @@ -49,12 +49,12 @@ install: - IF %PHP%==1 echo extension=php_pdo_sqlite.dll >> php.ini-max - IF %PHP%==1 echo extension=php_curl.dll >> php.ini-max - IF %PHP%==1 echo curl.cainfo=c:\php\cacert.pem >> php.ini-max - - appveyor DownloadFile https://getcomposer.org/composer.phar + - IF %PHP%==1 appveyor DownloadFile https://getcomposer.org/download/1.0.2/composer.phar - copy /Y php.ini-max php.ini - cd c:\projects\symfony - mkdir %APPDATA%\Composer - - IF %APPVEYOR_REPO_NAME%==symfony/symfony copy /Y .composer\* %APPDATA%\Composer\ - - IF %APPVEYOR_REPO_NAME%==symfony/symfony composer global install --no-progress --ansi || echo curl.cainfo needs PHP 5.3.7 + - copy /Y .composer\* %APPDATA%\Composer\ + - composer global install --no-progress --ansi - php phpunit install - IF %APPVEYOR_REPO_BRANCH%==master (SET COMPOSER_ROOT_VERSION=dev-master) ELSE (SET COMPOSER_ROOT_VERSION=%APPVEYOR_REPO_BRANCH%.x-dev) - composer update --no-progress --ansi diff --git a/composer.json b/composer.json index 571566f802bef..0b3d836e82389 100644 --- a/composer.json +++ b/composer.json @@ -82,7 +82,11 @@ }, "autoload": { "psr-4": { - "Symfony\\Bridge\\": "src/Symfony/Bridge/", + "Symfony\\Bridge\\Doctrine\\": "src/Symfony/Bridge/Doctrine/", + "Symfony\\Bridge\\Monolog\\": "src/Symfony/Bridge/Monolog/", + "Symfony\\Bridge\\ProxyManager\\": "src/Symfony/Bridge/ProxyManager/", + "Symfony\\Bridge\\Swiftmailer\\": "src/Symfony/Bridge/Swiftmailer/", + "Symfony\\Bridge\\Twig\\": "src/Symfony/Bridge/Twig/", "Symfony\\Bundle\\": "src/Symfony/Bundle/", "Symfony\\Component\\": "src/Symfony/Component/" }, @@ -95,6 +99,11 @@ "**/Tests/" ] }, + "autoload-dev": { + "psr-4": { + "Symfony\\Bridge\\PhpUnit\\": "src/Symfony/Bridge/PhpUnit/" + } + }, "minimum-stability": "dev", "extra": { "branch-alias": { diff --git a/phpunit b/phpunit index d2da42616d4eb..6d66f7b2b252a 100755 --- a/phpunit +++ b/phpunit @@ -51,6 +51,7 @@ if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit") || md5_file(__ $zip->extractTo(getcwd()); $zip->close(); chdir("phpunit-$PHPUNIT_VERSION"); + passthru("$COMPOSER remove --no-update phpspec/prophecy"); passthru("$COMPOSER remove --no-update symfony/yaml"); passthru("$COMPOSER require --dev --no-update symfony/phpunit-bridge \">=3.1@dev\""); passthru("$COMPOSER install --prefer-dist --no-progress --ansi", $exit); diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php index 25ead8c5577ec..36bdb1a48f6bc 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php @@ -261,7 +261,7 @@ public function testSubmitSingleExpandedNull() $field->submit(null); $this->assertNull($field->getData()); - $this->assertNull($field->getViewData()); + $this->assertSame('', $field->getViewData(), 'View data is always a string'); } public function testSubmitSingleNonExpandedNull() diff --git a/src/Symfony/Bridge/Doctrine/composer.json b/src/Symfony/Bridge/Doctrine/composer.json index f727880491c11..4c106a2568f53 100644 --- a/src/Symfony/Bridge/Doctrine/composer.json +++ b/src/Symfony/Bridge/Doctrine/composer.json @@ -22,7 +22,7 @@ "require-dev": { "symfony/stopwatch": "~2.2", "symfony/dependency-injection": "~2.2", - "symfony/form": "~2.7,>=2.7.1", + "symfony/form": "~2.7.12|~2.8.5", "symfony/http-kernel": "~2.2", "symfony/property-access": "~2.3", "symfony/security": "~2.2", diff --git a/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php b/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php index 267b012966901..2636bc3b8980e 100644 --- a/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php +++ b/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php @@ -41,7 +41,7 @@ public function onKernelResponse(FilterResponseEvent $event) return; } - if (!preg_match('{\bChrome/\d+[\.\d+]*\b}', $event->getRequest()->headers->get('User-Agent'))) { + if (!preg_match('{\b(?:Chrome/\d+(?:\.\d+)*|Firefox/(?:4[3-9]|[5-9]\d|\d{3,})(?:\.\d)*)\b}', $event->getRequest()->headers->get('User-Agent'))) { $this->sendHeaders = false; $this->headers = array(); diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php index b06b66bf34289..21fbc30ee68dd 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php @@ -160,7 +160,11 @@ public static function register($mode = false) private static function hasColorSupport() { if ('\\' === DIRECTORY_SEPARATOR) { - return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); + return + 0 >= version_compare('10.0.10586', PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD) + || false !== getenv('ANSICON') + || 'ON' === getenv('ConEmuANSI') + || 'xterm' === getenv('TERM'); } return defined('STDOUT') && function_exists('posix_isatty') && @posix_isatty(STDOUT); diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig index ae5d7fbed4a4a..4e5b9dd598302 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig @@ -314,10 +314,10 @@ {%- block widget_attributes -%} id="{{ id }}" name="{{ full_name }}" - {%- if read_only and attr.readonly is not defined %} readonly="readonly"{% endif -%} + {%- if read_only %} readonly="readonly"{% endif -%} {%- if disabled %} disabled="disabled"{% endif -%} {%- if required %} required="required"{% endif -%} - {%- for attrname, attrvalue in attr -%} + {%- for attrname, attrvalue in attr if 'readonly' != attrname or not read_only -%} {{- " " -}} {%- if attrname in ['placeholder', 'title'] -%} {{- attrname }}="{{ attrvalue|trans({}, translation_domain) }}" diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php index 8102cd8fa074f..edfa6d9cc94d5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php @@ -80,7 +80,7 @@ protected function configure() php %command.full_name% --parameters -Display a specific parameter by specifying his name with the --parameter option: +Display a specific parameter by specifying its name with the --parameter option: php %command.full_name% --parameter=kernel.debug @@ -99,25 +99,21 @@ protected function execute(InputInterface $input, OutputInterface $output) } $this->validateInput($input); + $object = $this->getContainerBuilder(); if ($input->getOption('parameters')) { - $object = $this->getContainerBuilder()->getParameterBag(); + $object = $object->getParameterBag(); $options = array(); } elseif ($parameter = $input->getOption('parameter')) { - $object = $this->getContainerBuilder(); $options = array('parameter' => $parameter); } elseif ($input->getOption('tags')) { - $object = $this->getContainerBuilder(); $options = array('group_by' => 'tags', 'show_private' => $input->getOption('show-private')); } elseif ($tag = $input->getOption('tag')) { - $object = $this->getContainerBuilder(); $options = array('tag' => $tag, 'show_private' => $input->getOption('show-private')); } elseif ($name = $input->getArgument('name')) { - $object = $this->getContainerBuilder(); $name = $this->findProperServiceName($input, $output, $object, $name); $options = array('id' => $name); } else { - $object = $this->getContainerBuilder(); $options = array('show_private' => $input->getOption('show-private')); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php index 22d0bde4d42e7..0fe5fa70f59bb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php @@ -227,7 +227,6 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o $alias = $definition; $table->addRow(array_merge(array($serviceId, sprintf('alias for "%s"', $alias)), $tagsCount ? array_fill(0, $tagsCount, '') : array())); } else { - // we have no information (happens with "service_container") $table->addRow(array_merge(array($serviceId, get_class($definition)), $tagsCount ? array_fill(0, $tagsCount, '') : array())); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php index 6804ded30d0e5..31593b9b80506 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php @@ -78,6 +78,10 @@ protected function createController($controller) */ protected function instantiateController($class) { + if ($this->container->has($class)) { + return $this->container->get($class); + } + $controller = parent::instantiateController($class); if ($controller instanceof ContainerAwareInterface) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_attributes.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_attributes.html.php index 14e65a7991c53..e90298701c682 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_attributes.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_attributes.html.php @@ -1,7 +1,8 @@ -id="escape($id) ?>" name="escape($full_name) ?>" readonly="readonly" +id="escape($id) ?>" name="escape($full_name) ?>" readonly="readonly" disabled="disabled" required="required" $v): ?> + escape($k), $view->escape($view['translator']->trans($v, array(), $translation_domain))) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php index 76a3489fe1551..b4dc8ada555b6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php @@ -87,6 +87,8 @@ public function testGetControllerService() public function testGetControllerInvokableService() { + $invokableController = new InvokableController('bar'); + $container = $this->createMockContainer(); $container->expects($this->once()) ->method('has') @@ -96,7 +98,7 @@ public function testGetControllerInvokableService() $container->expects($this->once()) ->method('get') ->with('foo') - ->will($this->returnValue($this)) + ->will($this->returnValue($invokableController)) ; $resolver = $this->createControllerResolver(null, null, $container); @@ -105,7 +107,33 @@ public function testGetControllerInvokableService() $controller = $resolver->getController($request); - $this->assertInstanceOf(get_class($this), $controller); + $this->assertEquals($invokableController, $controller); + } + + public function testGetControllerInvokableServiceWithClassNameAsName() + { + $invokableController = new InvokableController('bar'); + $className = __NAMESPACE__.'\InvokableController'; + + $container = $this->createMockContainer(); + $container->expects($this->once()) + ->method('has') + ->with($className) + ->will($this->returnValue(true)) + ; + $container->expects($this->once()) + ->method('get') + ->with($className) + ->will($this->returnValue($invokableController)) + ; + + $resolver = $this->createControllerResolver(null, null, $container); + $request = Request::create('/'); + $request->attributes->set('_controller', $className); + + $controller = $resolver->getController($request); + + $this->assertEquals($invokableController, $controller); } /** @@ -178,3 +206,14 @@ public function __invoke() { } } + +class InvokableController +{ + public function __construct($bar) // mandatory argument to prevent automatic instantiation + { + } + + public function __invoke() + { + } +} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php b/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php index 2be420ba8b0b0..d77eb279f4a69 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\WebProfilerBundle\Controller; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Matcher\UrlMatcherInterface; use Symfony\Component\Routing\Matcher\TraceableUrlMatcher; diff --git a/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php b/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php index 71c5090fc8a53..ee537af4f9889 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php +++ b/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php @@ -65,7 +65,7 @@ public function onKernelResponse(FilterResponseEvent $event) try { $response->headers->set( 'X-Debug-Token-Link', - $this->urlGenerator->generate('_profiler', array('token' => $response->headers->get('X-Debug-Token'))) + $this->urlGenerator->generate('_profiler', array('token' => $response->headers->get('X-Debug-Token')), UrlGeneratorInterface::ABSOLUTE_URL) ); } catch (\Exception $e) { $response->headers->set('X-Debug-Error', get_class($e).': '.$e->getMessage()); diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php index 446aefb793e89..e5d95471ae09f 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php @@ -16,6 +16,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; class WebDebugToolbarListenerTest extends \PHPUnit_Framework_TestCase { @@ -195,8 +196,8 @@ public function testXDebugUrlHeader() $urlGenerator ->expects($this->once()) ->method('generate') - ->with('_profiler', array('token' => 'xxxxxxxx')) - ->will($this->returnValue('/_profiler/xxxxxxxx')) + ->with('_profiler', array('token' => 'xxxxxxxx'), UrlGeneratorInterface::ABSOLUTE_URL) + ->will($this->returnValue('http://mydomain.com/_profiler/xxxxxxxx')) ; $event = new FilterResponseEvent($this->getKernelMock(), $this->getRequestMock(), HttpKernelInterface::MASTER_REQUEST, $response); @@ -204,7 +205,7 @@ public function testXDebugUrlHeader() $listener = new WebDebugToolbarListener($this->getTwigMock(), false, WebDebugToolbarListener::ENABLED, 'bottom', $urlGenerator); $listener->onKernelResponse($event); - $this->assertEquals('/_profiler/xxxxxxxx', $response->headers->get('X-Debug-Token-Link')); + $this->assertEquals('http://mydomain.com/_profiler/xxxxxxxx', $response->headers->get('X-Debug-Token-Link')); } public function testThrowingUrlGenerator() diff --git a/src/Symfony/Component/ClassLoader/ApcClassLoader.php b/src/Symfony/Component/ClassLoader/ApcClassLoader.php index 4f71ea173d50b..c21c64e38bfd2 100644 --- a/src/Symfony/Component/ClassLoader/ApcClassLoader.php +++ b/src/Symfony/Component/ClassLoader/ApcClassLoader.php @@ -122,8 +122,10 @@ public function loadClass($class) */ public function findFile($class) { - if (false === $file = apcu_fetch($this->prefix.$class)) { - apcu_store($this->prefix.$class, $file = $this->decorated->findFile($class)); + $file = apcu_fetch($this->prefix.$class, $success); + + if (!$success) { + apcu_store($this->prefix.$class, $file = $this->decorated->findFile($class) ?: null); } return $file; diff --git a/src/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php b/src/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php index aaafc78b85aed..0ab45678f9af1 100644 --- a/src/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php +++ b/src/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php @@ -92,8 +92,10 @@ public function __construct($prefix) */ public function findFile($class) { - if (false === $file = apcu_fetch($this->prefix.$class)) { - apcu_store($this->prefix.$class, $file = parent::findFile($class)); + $file = apcu_fetch($this->prefix.$class, $success); + + if (!$success) { + apcu_store($this->prefix.$class, $file = parent::findFile($class) ?: null); } return $file; diff --git a/src/Symfony/Component/ClassLoader/DebugClassLoader.php b/src/Symfony/Component/ClassLoader/DebugClassLoader.php index 92cbcb0d78014..783cf676f7060 100644 --- a/src/Symfony/Component/ClassLoader/DebugClassLoader.php +++ b/src/Symfony/Component/ClassLoader/DebugClassLoader.php @@ -89,7 +89,7 @@ public function unregister() */ public function findFile($class) { - return $this->classFinder->findFile($class); + return $this->classFinder->findFile($class) ?: null; } /** diff --git a/src/Symfony/Component/ClassLoader/WinCacheClassLoader.php b/src/Symfony/Component/ClassLoader/WinCacheClassLoader.php index 0fc11d019f862..8c846e3c266f8 100644 --- a/src/Symfony/Component/ClassLoader/WinCacheClassLoader.php +++ b/src/Symfony/Component/ClassLoader/WinCacheClassLoader.php @@ -123,8 +123,10 @@ public function loadClass($class) */ public function findFile($class) { - if (false === $file = wincache_ucache_get($this->prefix.$class)) { - wincache_ucache_set($this->prefix.$class, $file = $this->decorated->findFile($class), 0); + $file = wincache_ucache_get($this->prefix.$class, $success); + + if (!$success) { + wincache_ucache_set($this->prefix.$class, $file = $this->decorated->findFile($class) ?: null, 0); } return $file; diff --git a/src/Symfony/Component/ClassLoader/XcacheClassLoader.php b/src/Symfony/Component/ClassLoader/XcacheClassLoader.php index bf512273ef5fe..11da39d6f3ec4 100644 --- a/src/Symfony/Component/ClassLoader/XcacheClassLoader.php +++ b/src/Symfony/Component/ClassLoader/XcacheClassLoader.php @@ -126,7 +126,7 @@ public function findFile($class) if (xcache_isset($this->prefix.$class)) { $file = xcache_get($this->prefix.$class); } else { - $file = $this->decorated->findFile($class); + $file = $this->decorated->findFile($class) ?: null; xcache_set($this->prefix.$class, $file); } diff --git a/src/Symfony/Component/Config/Tests/Util/XmlUtilsTest.php b/src/Symfony/Component/Config/Tests/Util/XmlUtilsTest.php index 2a7308bef86df..7ce5418994773 100644 --- a/src/Symfony/Component/Config/Tests/Util/XmlUtilsTest.php +++ b/src/Symfony/Component/Config/Tests/Util/XmlUtilsTest.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Config\Tests\Loader; +namespace Symfony\Component\Config\Tests\Util; use Symfony\Component\Config\Util\XmlUtils; diff --git a/src/Symfony/Component/Console/Helper/Helper.php b/src/Symfony/Component/Console/Helper/Helper.php index 156d8dc644cf9..444bbffc768e3 100644 --- a/src/Symfony/Component/Console/Helper/Helper.php +++ b/src/Symfony/Component/Console/Helper/Helper.php @@ -66,26 +66,28 @@ public static function formatTime($secs) { static $timeFormats = array( array(0, '< 1 sec'), - array(2, '1 sec'), - array(59, 'secs', 1), + array(1, '1 sec'), + array(2, 'secs', 1), array(60, '1 min'), - array(3600, 'mins', 60), - array(5400, '1 hr'), - array(86400, 'hrs', 3600), - array(129600, '1 day'), - array(604800, 'days', 86400), + array(120, 'mins', 60), + array(3600, '1 hr'), + array(7200, 'hrs', 3600), + array(86400, '1 day'), + array(172800, 'days', 86400), ); - foreach ($timeFormats as $format) { + foreach ($timeFormats as $index => $format) { if ($secs >= $format[0]) { - continue; + if ((isset($timeFormats[$index + 1]) && $secs < $timeFormats[$index + 1][0]) + || $index == count($timeFormats) - 1 + ) { + if (2 == count($format)) { + return $format[1]; + } + + return floor($secs / $format[2]).' '.$format[1]; + } } - - if (2 == count($format)) { - return $format[1]; - } - - return ceil($secs / $format[2]).' '.$format[1]; } } diff --git a/src/Symfony/Component/Console/Helper/ProgressBar.php b/src/Symfony/Component/Console/Helper/ProgressBar.php index 016e885f4b288..1b2eefd29e0a3 100644 --- a/src/Symfony/Component/Console/Helper/ProgressBar.php +++ b/src/Symfony/Component/Console/Helper/ProgressBar.php @@ -40,7 +40,6 @@ class ProgressBar private $startTime; private $stepWidth; private $percent = 0.0; - private $lastMessagesLength = 0; private $formatLineCount; private $messages; private $overwrite = true; @@ -472,7 +471,7 @@ public function clear() $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat()); } - $this->overwrite(str_repeat("\n", $this->formatLineCount)); + $this->overwrite(''); } /** @@ -512,37 +511,22 @@ private function setMaxSteps($max) */ private function overwrite($message) { - $lines = explode("\n", $message); - - // append whitespace to match the line's length - if (null !== $this->lastMessagesLength) { - foreach ($lines as $i => $line) { - if ($this->lastMessagesLength > Helper::strlenWithoutDecoration($this->output->getFormatter(), $line)) { - $lines[$i] = str_pad($line, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT); - } - } - } - if ($this->overwrite) { - // move back to the beginning of the progress bar before redrawing it + // Move the cursor to the beginning of the line $this->output->write("\x0D"); - } elseif ($this->step > 0) { - // move to new line - $this->output->writeln(''); - } - if ($this->formatLineCount) { - $this->output->write(sprintf("\033[%dA", $this->formatLineCount)); - } - $this->output->write(implode("\n", $lines)); + // Erase the line + $this->output->write("\x1B[2K"); - $this->lastMessagesLength = 0; - foreach ($lines as $line) { - $len = Helper::strlenWithoutDecoration($this->output->getFormatter(), $line); - if ($len > $this->lastMessagesLength) { - $this->lastMessagesLength = $len; + // Erase previous lines + if ($this->formatLineCount > 0) { + $this->output->write(str_repeat("\x1B[1A\x1B[2K", $this->formatLineCount)); } + } elseif ($this->step > 0) { + $this->output->writeln(''); } + + $this->output->write($message); } private function determineBestFormat() diff --git a/src/Symfony/Component/Console/Output/StreamOutput.php b/src/Symfony/Component/Console/Output/StreamOutput.php index a1facda2de539..80c352a278671 100644 --- a/src/Symfony/Component/Console/Output/StreamOutput.php +++ b/src/Symfony/Component/Console/Output/StreamOutput.php @@ -83,7 +83,7 @@ protected function doWrite($message, $newline) * * Colorization is disabled if not supported by the stream: * - * - Windows without Ansicon, ConEmu or Mintty + * - Windows before 10.0.10586 without Ansicon, ConEmu or Mintty * - non tty consoles * * @return bool true if the stream supports colorization, false otherwise @@ -91,7 +91,11 @@ protected function doWrite($message, $newline) protected function hasColorSupport() { if (DIRECTORY_SEPARATOR === '\\') { - return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); + return + 0 >= version_compare('10.0.10586', PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD) + || false !== getenv('ANSICON') + || 'ON' === getenv('ConEmuANSI') + || 'xterm' === getenv('TERM'); } return function_exists('posix_isatty') && @posix_isatty($this->stream); diff --git a/src/Symfony/Component/Console/Style/SymfonyStyle.php b/src/Symfony/Component/Console/Style/SymfonyStyle.php index 365c03ae328f9..21fc3b92cfee0 100644 --- a/src/Symfony/Component/Console/Style/SymfonyStyle.php +++ b/src/Symfony/Component/Console/Style/SymfonyStyle.php @@ -17,6 +17,7 @@ use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Helper\SymfonyQuestionHelper; use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Helper\TableCell; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\BufferedOutput; use Symfony\Component\Console\Output\OutputInterface; @@ -204,7 +205,16 @@ public function caution($message) */ public function table(array $headers, array $rows) { - $headers = array_map(function ($value) { return sprintf('%s', $value); }, $headers); + array_walk_recursive($headers, function (&$value) { + if ($value instanceof TableCell) { + $value = new TableCell(sprintf('%s', $value), array( + 'colspan' => $value->getColspan(), + 'rowspan' => $value->getRowspan(), + )); + } else { + $value = sprintf('%s', $value); + } + }); $table = new Table($this); $table->setHeaders($headers); diff --git a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_8.php b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_8.php new file mode 100644 index 0000000000000..0244fd27256f2 --- /dev/null +++ b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_8.php @@ -0,0 +1,26 @@ + 3))), + array('ISBN', 'Title', 'Author'), + ); + + $rows = array( + array( + '978-0521567817', + 'De Monarchia', + new TableCell("Dante Alighieri\nspans multiple rows", array('rowspan' => 2)), + ), + array('978-0804169127', 'Divine Comedy'), + ); + + $output = new SymfonyStyleWithForcedLineLength($input, $output); + $output->table($headers, $rows); +}; diff --git a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_8.txt b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_8.txt new file mode 100644 index 0000000000000..005b846eae491 --- /dev/null +++ b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_8.txt @@ -0,0 +1,9 @@ + ---------------- --------------- --------------------- + Main table title + ---------------- --------------- --------------------- + ISBN Title Author + ---------------- --------------- --------------------- + 978-0521567817 De Monarchia Dante Alighieri + 978-0804169127 Divine Comedy spans multiple rows + ---------------- --------------- --------------------- + diff --git a/src/Symfony/Component/Console/Tests/Helper/HelperTest.php b/src/Symfony/Component/Console/Tests/Helper/HelperTest.php new file mode 100644 index 0000000000000..33fa22051a05d --- /dev/null +++ b/src/Symfony/Component/Console/Tests/Helper/HelperTest.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\Helper; + +class HelperTest extends \PHPUnit_Framework_TestCase +{ + public function formatTimeProvider() + { + return array( + array(0, '< 1 sec'), + array(1, '1 sec'), + array(2, '2 secs'), + array(59, '59 secs'), + array(60, '1 min'), + array(61, '1 min'), + array(119, '1 min'), + array(120, '2 mins'), + array(121, '2 mins'), + array(3599, '59 mins'), + array(3600, '1 hr'), + array(7199, '1 hr'), + array(7200, '2 hrs'), + array(7201, '2 hrs'), + array(86399, '23 hrs'), + array(86400, '1 day'), + array(86401, '1 day'), + array(172799, '1 day'), + array(172800, '2 days'), + array(172801, '2 days'), + ); + } + + /** + * @dataProvider formatTimeProvider + * + * @param int $secs + * @param string $expectedFormat + */ + public function testFormatTime($secs, $expectedFormat) + { + $this->assertEquals($expectedFormat, Helper::formatTime($secs)); + } +} diff --git a/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php b/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php index ffe5bed086348..04f3dbe4a73c3 100644 --- a/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php @@ -233,7 +233,7 @@ public function testOverwriteWithShorterLine() $this->generateOutput(' 0/50 [>---------------------------] 0%'). $this->generateOutput(' 0/50 [>---------------------------] 0%'). $this->generateOutput(' 1/50 [>---------------------------] 2%'). - $this->generateOutput(' 2/50 [=>--------------------------] '), + $this->generateOutput(' 2/50 [=>--------------------------]'), stream_get_contents($output->getStream()) ); } @@ -356,7 +356,7 @@ public function testClear() $this->assertEquals( $this->generateOutput(' 0/50 [>---------------------------] 0%'). $this->generateOutput(' 25/50 [==============>-------------] 50%'). - $this->generateOutput(' '), + $this->generateOutput(''), stream_get_contents($output->getStream()) ); } @@ -554,9 +554,9 @@ public function testMultilineFormat() rewind($output->getStream()); $this->assertEquals( $this->generateOutput(">---------------------------\nfoobar"). - $this->generateOutput("=========>------------------\nfoobar "). - $this->generateOutput(" \n "). - $this->generateOutput("============================\nfoobar "), + $this->generateOutput("=========>------------------\nfoobar"). + "\x0D\x1B[2K\x1B[1A\x1B[2K". + $this->generateOutput("============================\nfoobar"), stream_get_contents($output->getStream()) ); } @@ -591,17 +591,17 @@ public function testAnsiColorsAndEmojis() $this->generateOutput( " \033[44;37m Starting the demo... fingers crossed \033[0m\n". ' 0/15 '.$progress.str_repeat($empty, 26)." 0%\n". - " \xf0\x9f\x8f\x81 1 sec \033[44;37m 0 B \033[0m" + " \xf0\x9f\x8f\x81 < 1 sec \033[44;37m 0 B \033[0m" ). $this->generateOutput( " \033[44;37m Looks good to me... \033[0m\n". ' 4/15 '.str_repeat($done, 7).$progress.str_repeat($empty, 19)." 26%\n". - " \xf0\x9f\x8f\x81 1 sec \033[41;37m 97 KiB \033[0m" + " \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 97 KiB \033[0m" ). $this->generateOutput( " \033[44;37m Thanks, bye \033[0m\n". ' 15/15 '.str_repeat($done, 28)." 100%\n". - " \xf0\x9f\x8f\x81 1 sec \033[41;37m 195 KiB \033[0m" + " \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 195 KiB \033[0m" ), stream_get_contents($output->getStream()) ); @@ -665,6 +665,6 @@ protected function generateOutput($expected) { $count = substr_count($expected, "\n"); - return "\x0D".($count ? sprintf("\033[%dA", $count) : '').$expected; + return "\x0D\x1B[2K".($count ? str_repeat("\x1B[1A\x1B[2K", $count) : '').$expected; } } diff --git a/src/Symfony/Component/Debug/DebugClassLoader.php b/src/Symfony/Component/Debug/DebugClassLoader.php index fc036a628658f..36afa369301b2 100644 --- a/src/Symfony/Component/Debug/DebugClassLoader.php +++ b/src/Symfony/Component/Debug/DebugClassLoader.php @@ -51,15 +51,25 @@ public function __construct($classLoader) } if (!isset(self::$caseCheck)) { - if(!file_exists(strtolower(__FILE__))) { + $file = file_exists(__FILE__) ? __FILE__ : rtrim(realpath('.'), DIRECTORY_SEPARATOR); + $i = strrpos($file, DIRECTORY_SEPARATOR); + $dir = substr($file, 0, 1 + $i); + $file = substr($file, 1 + $i); + $test = strtoupper($file) === $file ? strtolower($file) : strtoupper($file); + $test = realpath($dir.$test); + + if (false === $test || false === $i) { // filesystem is case sensitive self::$caseCheck = 0; - } elseif(realpath(strtolower(__FILE__)) === __FILE__) { - // filesystem is not case sensitive + } elseif (substr($test, -strlen($file)) === $file) { + // filesystem is case insensitive and realpath() normalizes the case of characters self::$caseCheck = 1; - } else { - // filesystem is not case sensitive AND realpath() fails to normalize case + } elseif (false !== stripos(PHP_OS, 'darwin')) { + // on MacOSX, HFS+ is case insensitive but realpath() doesn't normalize the case of characters self::$caseCheck = 2; + } else { + // filesystem case checks failed, fallback to disabling them + self::$caseCheck = 0; } } } diff --git a/src/Symfony/Component/DependencyInjection/Alias.php b/src/Symfony/Component/DependencyInjection/Alias.php index 5a6c2feda7f4e..eaf7f00ccd446 100644 --- a/src/Symfony/Component/DependencyInjection/Alias.php +++ b/src/Symfony/Component/DependencyInjection/Alias.php @@ -17,8 +17,6 @@ class Alias private $public; /** - * Constructor. - * * @param string $id Alias identifier * @param bool $public If this alias is public */ diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php index 5120eb6215c1a..42b491c7c70e8 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php @@ -34,8 +34,6 @@ class AnalyzeServiceReferencesPass implements RepeatablePassInterface private $onlyConstructorArguments; /** - * Constructor. - * * @param bool $onlyConstructorArguments Sets this Service Reference pass to ignore method calls */ public function __construct($onlyConstructorArguments = false) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php b/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php index 4e4c2cdaba5b9..1f6304ee82e13 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php @@ -25,9 +25,6 @@ class Compiler private $loggingFormatter; private $serviceReferenceGraph; - /** - * Constructor. - */ public function __construct() { $this->passConfig = new PassConfig(); diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php index 2849dfe129a78..ab5f309f2e06b 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php @@ -35,9 +35,6 @@ class PassConfig private $optimizationPasses; private $removingPasses; - /** - * Constructor. - */ public function __construct() { $this->mergePass = new MergeExtensionConfigurationPass(); @@ -57,8 +54,8 @@ public function __construct() $this->removingPasses = array( new RemovePrivateAliasesPass(), - new RemoveAbstractDefinitionsPass(), new ReplaceAliasByActualDefinitionPass(), + new RemoveAbstractDefinitionsPass(), new RepeatedPass(array( new AnalyzeServiceReferencesPass(), new InlineServiceDefinitionsPass(), @@ -101,8 +98,7 @@ public function addPass(CompilerPassInterface $pass, $type = self::TYPE_BEFORE_O throw new InvalidArgumentException(sprintf('Invalid type "%s".', $type)); } - $passes = &$this->$property; - $passes[] = $pass; + $this->{$property}[] = $pass; } /** diff --git a/src/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php b/src/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php index e34b0681e5389..508a8978ea68b 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php @@ -32,8 +32,6 @@ class RepeatedPass implements CompilerPassInterface private $passes; /** - * Constructor. - * * @param RepeatablePassInterface[] $passes An array of RepeatablePassInterface objects * * @throws InvalidArgumentException when the passes don't implement RepeatablePassInterface diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php index c5332fcd8d987..e80d3ccbf7b53 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php @@ -25,7 +25,6 @@ class ReplaceAliasByActualDefinitionPass implements CompilerPassInterface { private $compiler; private $formatter; - private $sourceId; /** * Process the Container to replace aliases with service definitions. @@ -36,114 +35,110 @@ class ReplaceAliasByActualDefinitionPass implements CompilerPassInterface */ public function process(ContainerBuilder $container) { + // Setup $this->compiler = $container->getCompiler(); $this->formatter = $this->compiler->getLoggingFormatter(); - - foreach ($container->getAliases() as $id => $alias) { - $aliasId = (string) $alias; - + // First collect all alias targets that need to be replaced + $seenAliasTargets = array(); + $replacements = array(); + foreach ($container->getAliases() as $definitionId => $target) { + $targetId = (string) $target; + // Special case: leave this target alone + if ('service_container' === $targetId) { + continue; + } + // Check if target needs to be replaces + if (isset($replacements[$targetId])) { + $container->setAlias($definitionId, $replacements[$targetId]); + } + // No neeed to process the same target twice + if (isset($seenAliasTargets[$targetId])) { + continue; + } + // Process new target + $seenAliasTargets[$targetId] = true; try { - $definition = $container->getDefinition($aliasId); + $definition = $container->getDefinition($targetId); } catch (InvalidArgumentException $e) { - throw new InvalidArgumentException(sprintf('Unable to replace alias "%s" with actual definition "%s".', $id, $alias), null, $e); + throw new InvalidArgumentException(sprintf('Unable to replace alias "%s" with actual definition "%s".', $definitionId, $targetId), null, $e); } - if ($definition->isPublic()) { continue; } - + // Remove private definition and schedule for replacement $definition->setPublic(true); - $container->setDefinition($id, $definition); - $container->removeDefinition($aliasId); - - $this->updateReferences($container, $aliasId, $id); - - // we have to restart the process due to concurrent modification of - // the container - $this->process($container); - - break; - } - } - - /** - * Updates references to remove aliases. - * - * @param ContainerBuilder $container The container - * @param string $currentId The alias identifier being replaced - * @param string $newId The id of the service the alias points to - */ - private function updateReferences($container, $currentId, $newId) - { - foreach ($container->getAliases() as $id => $alias) { - if ($currentId === (string) $alias) { - $container->setAlias($id, $newId); - } + $container->setDefinition($definitionId, $definition); + $container->removeDefinition($targetId); + $replacements[$targetId] = $definitionId; } - foreach ($container->getDefinitions() as $id => $definition) { - $this->sourceId = $id; - - $definition->setArguments( - $this->updateArgumentReferences($definition->getArguments(), $currentId, $newId) - ); - - $definition->setMethodCalls( - $this->updateArgumentReferences($definition->getMethodCalls(), $currentId, $newId) - ); - - $definition->setProperties( - $this->updateArgumentReferences($definition->getProperties(), $currentId, $newId) - ); - - $definition->setFactoryService($this->updateFactoryServiceReference($definition->getFactoryService(false), $currentId, $newId), false); - $definition->setFactory($this->updateFactoryReference($definition->getFactory(), $currentId, $newId)); + // Now replace target instances in all definitions + foreach ($container->getDefinitions() as $definitionId => $definition) { + $definition->setArguments($this->updateArgumentReferences($replacements, $definitionId, $definition->getArguments())); + $definition->setMethodCalls($this->updateArgumentReferences($replacements, $definitionId, $definition->getMethodCalls())); + $definition->setProperties($this->updateArgumentReferences($replacements, $definitionId, $definition->getProperties())); + $definition->setFactoryService($this->updateFactoryReferenceId($replacements, $definition->getFactoryService(false)), false); + $definition->setFactory($this->updateFactoryReference($replacements, $definition->getFactory())); } } /** - * Updates argument references. + * Recursively updates references in an array. * - * @param array $arguments An array of Arguments - * @param string $currentId The alias identifier - * @param string $newId The identifier the alias points to + * @param array $replacements Table of aliases to replace + * @param string $definitionId Identifier of this definition + * @param array $arguments Where to replace the aliases * * @return array */ - private function updateArgumentReferences(array $arguments, $currentId, $newId) + private function updateArgumentReferences(array $replacements, $definitionId, array $arguments) { foreach ($arguments as $k => $argument) { + // Handle recursion step if (is_array($argument)) { - $arguments[$k] = $this->updateArgumentReferences($argument, $currentId, $newId); - } elseif ($argument instanceof Reference) { - if ($currentId === (string) $argument) { - $arguments[$k] = new Reference($newId, $argument->getInvalidBehavior()); - $this->compiler->addLogMessage($this->formatter->formatUpdateReference($this, $this->sourceId, $currentId, $newId)); - } + $arguments[$k] = $this->updateArgumentReferences($replacements, $definitionId, $argument); + continue; } + // Skip arguments that don't need replacement + if (!$argument instanceof Reference) { + continue; + } + $referenceId = (string) $argument; + if (!isset($replacements[$referenceId])) { + continue; + } + // Perform the replacement + $newId = $replacements[$referenceId]; + $arguments[$k] = new Reference($newId, $argument->getInvalidBehavior()); + $this->compiler->addLogMessage($this->formatter->formatUpdateReference($this, $definitionId, $referenceId, $newId)); } return $arguments; } - private function updateFactoryServiceReference($factoryService, $currentId, $newId) + /** + * Returns the updated reference for the factory service. + * + * @param array $replacements Table of aliases to replace + * @param string|null $referenceId Factory service reference identifier + * + * @return string|null + */ + private function updateFactoryReferenceId(array $replacements, $referenceId) { - if (null === $factoryService) { + if (null === $referenceId) { return; } - return $currentId === $factoryService ? $newId : $factoryService; + return isset($replacements[$referenceId]) ? $replacements[$referenceId] : $referenceId; } - private function updateFactoryReference($factory, $currentId, $newId) + private function updateFactoryReference(array $replacements, $factory) { - if (null === $factory || !is_array($factory) || !$factory[0] instanceof Reference) { - return $factory; + if (is_array($factory) && $factory[0] instanceof Reference && isset($replacements[$referenceId = (string) $factory[0]])) { + $factory[0] = new Reference($replacements[$referenceId], $factory[0]->getInvalidBehavior()); } - if ($currentId === (string) $factory[0]) { - $factory[0] = new Reference($newId, $factory[0]->getInvalidBehavior()); - } return $factory; } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphEdge.php b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphEdge.php index 6a3e2ea5697d3..056be7fa3f5d4 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphEdge.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphEdge.php @@ -25,8 +25,6 @@ class ServiceReferenceGraphEdge private $value; /** - * Constructor. - * * @param ServiceReferenceGraphNode $sourceNode * @param ServiceReferenceGraphNode $destNode * @param string $value diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.php b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.php index c49c932575f03..e5718b2b6d59a 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.php @@ -29,8 +29,6 @@ class ServiceReferenceGraphNode private $value; /** - * Constructor. - * * @param string $id The node identifier * @param mixed $value The node value */ diff --git a/src/Symfony/Component/DependencyInjection/Container.php b/src/Symfony/Component/DependencyInjection/Container.php index 690975f96b3b1..553f9120e1385 100644 --- a/src/Symfony/Component/DependencyInjection/Container.php +++ b/src/Symfony/Component/DependencyInjection/Container.php @@ -77,8 +77,6 @@ class Container implements IntrospectableContainerInterface private $underscoreMap = array('_' => '', '.' => '_', '\\' => '_'); /** - * Constructor. - * * @param ParameterBagInterface $parameterBag A ParameterBagInterface instance */ public function __construct(ParameterBagInterface $parameterBag = null) diff --git a/src/Symfony/Component/DependencyInjection/ContainerAware.php b/src/Symfony/Component/DependencyInjection/ContainerAware.php index 8937c669a6a63..28df5570b874e 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerAware.php +++ b/src/Symfony/Component/DependencyInjection/ContainerAware.php @@ -24,9 +24,7 @@ abstract class ContainerAware implements ContainerAwareInterface protected $container; /** - * Sets the container. - * - * @param ContainerInterface|null $container A ContainerInterface instance or null + * {@inheritdoc} */ public function setContainer(ContainerInterface $container = null) { diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php index 0c802b522f36e..d5238f34400ab 100644 --- a/src/Symfony/Component/DependencyInjection/Definition.php +++ b/src/Symfony/Component/DependencyInjection/Definition.php @@ -42,8 +42,6 @@ class Definition protected $arguments; /** - * Constructor. - * * @param string|null $class The service class * @param array $arguments An array of arguments to pass to the service constructor */ diff --git a/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php b/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php index b3f3fee504132..66e705f56f6a5 100644 --- a/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php +++ b/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php @@ -25,8 +25,6 @@ class DefinitionDecorator extends Definition private $changes = array(); /** - * Constructor. - * * @param string $parent The id of Definition instance to decorate. */ public function __construct($parent) diff --git a/src/Symfony/Component/DependencyInjection/Dumper/Dumper.php b/src/Symfony/Component/DependencyInjection/Dumper/Dumper.php index 4b9d586f2c934..a39a5c744ba78 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/Dumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/Dumper.php @@ -23,8 +23,6 @@ abstract class Dumper implements DumperInterface protected $container; /** - * Constructor. - * * @param ContainerBuilder $container The service container to dump */ public function __construct(ContainerBuilder $container) diff --git a/src/Symfony/Component/DependencyInjection/Exception/ParameterNotFoundException.php b/src/Symfony/Component/DependencyInjection/Exception/ParameterNotFoundException.php index b529f0fe83d22..ab7b86d5acc90 100644 --- a/src/Symfony/Component/DependencyInjection/Exception/ParameterNotFoundException.php +++ b/src/Symfony/Component/DependencyInjection/Exception/ParameterNotFoundException.php @@ -24,8 +24,6 @@ class ParameterNotFoundException extends InvalidArgumentException private $alternatives; /** - * Constructor. - * * @param string $key The requested parameter key * @param string $sourceId The service id that references the non-existent parameter * @param string $sourceKey The parameter key that references the non-existent parameter diff --git a/src/Symfony/Component/DependencyInjection/Extension/Extension.php b/src/Symfony/Component/DependencyInjection/Extension/Extension.php index 20ea1002cb82c..ced39f7281b6c 100644 --- a/src/Symfony/Component/DependencyInjection/Extension/Extension.php +++ b/src/Symfony/Component/DependencyInjection/Extension/Extension.php @@ -27,9 +27,7 @@ abstract class Extension implements ExtensionInterface, ConfigurationExtensionInterface { /** - * Returns the base path for the XSD files. - * - * @return string The XSD base path + * {@inheritdoc} */ public function getXsdValidationBasePath() { @@ -37,9 +35,7 @@ public function getXsdValidationBasePath() } /** - * Returns the namespace to be used for this extension (XML namespace). - * - * @return string The XML namespace + * {@inheritdoc} */ public function getNamespace() { diff --git a/src/Symfony/Component/DependencyInjection/Loader/ClosureLoader.php b/src/Symfony/Component/DependencyInjection/Loader/ClosureLoader.php index a5b4e5ad240e5..df70cdf44f283 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/ClosureLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/ClosureLoader.php @@ -26,8 +26,6 @@ class ClosureLoader extends Loader private $container; /** - * Constructor. - * * @param ContainerBuilder $container A ContainerBuilder instance */ public function __construct(ContainerBuilder $container) diff --git a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php index d71eecf74156f..90cd6bcfafa4d 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php @@ -25,8 +25,6 @@ abstract class FileLoader extends BaseFileLoader protected $container; /** - * Constructor. - * * @param ContainerBuilder $container A ContainerBuilder instance * @param FileLocatorInterface $locator A FileLocator instance */ diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 08df93330107c..f4ec265d533f7 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -302,11 +302,7 @@ private function processAnonymousServices(\DOMDocument $xml, $file) // give it a unique name $id = sprintf('%s_%d', hash('sha256', $file), ++$count); $node->setAttribute('id', $id); - - if ($services = $this->getChildren($node, 'service')) { - $definitions[$id] = array($node, $file, true); - $services[0]->setAttribute('id', $id); - } + $definitions[$id] = array($node, $file, true); } } diff --git a/src/Symfony/Component/DependencyInjection/Parameter.php b/src/Symfony/Component/DependencyInjection/Parameter.php index 5431ed8221cf4..cac6f6c4c2264 100644 --- a/src/Symfony/Component/DependencyInjection/Parameter.php +++ b/src/Symfony/Component/DependencyInjection/Parameter.php @@ -21,8 +21,6 @@ class Parameter private $id; /** - * Constructor. - * * @param string $id The parameter key */ public function __construct($id) @@ -31,8 +29,6 @@ public function __construct($id) } /** - * __toString. - * * @return string The parameter key */ public function __toString() diff --git a/src/Symfony/Component/DependencyInjection/ParameterBag/FrozenParameterBag.php b/src/Symfony/Component/DependencyInjection/ParameterBag/FrozenParameterBag.php index d9fe9eceb69b9..ad65ad960f527 100644 --- a/src/Symfony/Component/DependencyInjection/ParameterBag/FrozenParameterBag.php +++ b/src/Symfony/Component/DependencyInjection/ParameterBag/FrozenParameterBag.php @@ -21,8 +21,6 @@ class FrozenParameterBag extends ParameterBag { /** - * Constructor. - * * For performance reasons, the constructor assumes that * all keys are already lowercased. * diff --git a/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php b/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php index 64a7789d1a859..0611b1f69e322 100644 --- a/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php +++ b/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php @@ -26,8 +26,6 @@ class ParameterBag implements ParameterBagInterface protected $resolved = false; /** - * Constructor. - * * @param array $parameters An array of parameters */ public function __construct(array $parameters = array()) @@ -56,9 +54,7 @@ public function add(array $parameters) } /** - * Gets the service container parameters. - * - * @return array An array of parameters + * {@inheritdoc} */ public function all() { @@ -66,13 +62,7 @@ public function all() } /** - * Gets a service container parameter. - * - * @param string $name The parameter name - * - * @return mixed The parameter value - * - * @throws ParameterNotFoundException if the parameter is not defined + * {@inheritdoc} */ public function get($name) { @@ -109,11 +99,7 @@ public function set($name, $value) } /** - * Returns true if a parameter name is defined. - * - * @param string $name The parameter name - * - * @return bool true if the parameter name is defined, false otherwise + * {@inheritdoc} */ public function has($name) { @@ -131,7 +117,7 @@ public function remove($name) } /** - * Replaces parameter placeholders (%name%) by their values for all parameters. + * {@inheritdoc} */ public function resolve() { @@ -266,6 +252,9 @@ public function escapeValue($value) return $value; } + /** + * {@inheritdoc} + */ public function unescapeValue($value) { if (is_string($value)) { diff --git a/src/Symfony/Component/DependencyInjection/Reference.php b/src/Symfony/Component/DependencyInjection/Reference.php index 6a4824908c653..1798c5efe12c1 100644 --- a/src/Symfony/Component/DependencyInjection/Reference.php +++ b/src/Symfony/Component/DependencyInjection/Reference.php @@ -23,8 +23,6 @@ class Reference private $strict; /** - * Constructor. - * * @param string $id The service identifier * @param int $invalidBehavior The behavior when the service does not exist * @param bool $strict Sets how this reference is validated @@ -39,8 +37,6 @@ public function __construct($id, $invalidBehavior = ContainerInterface::EXCEPTIO } /** - * __toString. - * * @return string The service identifier */ public function __toString() diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ReplaceAliasByActualDefinitionPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ReplaceAliasByActualDefinitionPassTest.php index be58a1a175967..7cb63c6613663 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ReplaceAliasByActualDefinitionPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ReplaceAliasByActualDefinitionPassTest.php @@ -36,6 +36,8 @@ public function testProcess() $container->setAlias('a_alias', 'a'); $container->setAlias('b_alias', 'b'); + $container->setAlias('container', 'service_container'); + $this->process($container); $this->assertTrue($container->has('a'), '->process() does nothing to public definitions.'); @@ -47,6 +49,7 @@ public function testProcess() ); $this->assertSame('b_alias', $aDefinition->getFactoryService(false)); + $this->assertTrue($container->has('container')); $resolvedFactory = $aDefinition->getFactory(false); $this->assertSame('b_alias', (string) $resolvedFactory[0]); diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 044c27757e680..b066b4e611d4d 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -739,6 +739,21 @@ public function testExtensionConfig() $this->assertEquals(array($second, $first), $configs); } + public function testAbstractAlias() + { + $container = new ContainerBuilder(); + + $abstract = new Definition('AbstractClass'); + $abstract->setAbstract(true); + + $container->setDefinition('abstract_service', $abstract); + $container->setAlias('abstract_alias', 'abstract_service'); + + $container->compile(); + + $this->assertSame('abstract_service', (string) $container->getAlias('abstract_alias')); + } + public function testLazyLoadedService() { $loader = new ClosureLoader($container = new ContainerBuilder()); diff --git a/src/Symfony/Component/DependencyInjection/Tests/CrossCheckTest.php b/src/Symfony/Component/DependencyInjection/Tests/CrossCheckTest.php index 692d73dbcdc2e..7905e7f3c6b9c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/CrossCheckTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/CrossCheckTest.php @@ -73,24 +73,17 @@ public function testCrossCheck($fixture, $type) public function crossCheckLoadersDumpers() { - $tests = array( + return array( array('services1.xml', 'xml'), array('services2.xml', 'xml'), array('services6.xml', 'xml'), array('services8.xml', 'xml'), array('services9.xml', 'xml'), + array('services1.yml', 'yaml'), + array('services2.yml', 'yaml'), + array('services6.yml', 'yaml'), + array('services8.yml', 'yaml'), + array('services9.yml', 'yaml'), ); - - if (class_exists('Symfony\Component\Yaml\Yaml')) { - $tests = array_merge($tests, array( - array('services1.yml', 'yaml'), - array('services2.yml', 'yaml'), - array('services6.yml', 'yaml'), - array('services8.yml', 'yaml'), - array('services9.yml', 'yaml'), - )); - } - - return $tests; } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 7207a143e6f66..afe2f10ff50f0 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -16,6 +16,8 @@ use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Variable; +use Symfony\Component\ExpressionLanguage\Expression; class PhpDumperTest extends \PHPUnit_Framework_TestCase { @@ -85,14 +87,25 @@ public function testDumpRelativeDir() } /** + * @dataProvider provideInvalidParameters * @expectedException \InvalidArgumentException */ - public function testExportParameters() + public function testExportParameters($parameters) { - $dumper = new PhpDumper(new ContainerBuilder(new ParameterBag(array('foo' => new Reference('foo'))))); + $dumper = new PhpDumper(new ContainerBuilder(new ParameterBag($parameters))); $dumper->dump(); } + public function provideInvalidParameters() + { + return array( + array(array('foo' => new Definition('stdClass'))), + array(array('foo' => new Expression('service("foo").foo() ~ (container.hasparameter("foo") ? parameter("foo") : "default")'))), + array(array('foo' => new Reference('foo'))), + array(array('foo' => new Variable('foo'))), + ); + } + public function testAddParameters() { $container = include self::$fixturesPath.'/containers/container8.php'; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services5.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services5.xml index acb93e748e483..347df977dd26b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services5.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services5.xml @@ -14,8 +14,12 @@ - + + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index d82050d758273..cce41b03de212 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -163,7 +163,7 @@ public function testLoadAnonymousServices() $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); $loader->load('services5.xml'); $services = $container->getDefinitions(); - $this->assertCount(4, $services, '->load() attributes unique ids to anonymous services'); + $this->assertCount(6, $services, '->load() attributes unique ids to anonymous services'); // anonymous service as an argument $args = $services['foo']->getArguments(); @@ -172,6 +172,7 @@ public function testLoadAnonymousServices() $this->assertTrue(isset($services[(string) $args[0]]), '->load() makes a reference to the created ones'); $inner = $services[(string) $args[0]]; $this->assertEquals('BarClass', $inner->getClass(), '->load() uses the same configuration as for the anonymous ones'); + $this->assertFalse($inner->isPublic()); // inner anonymous services $args = $inner->getArguments(); @@ -188,7 +189,25 @@ public function testLoadAnonymousServices() $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Reference', $property, '->load() converts anonymous services to references to "normal" services'); $this->assertTrue(isset($services[(string) $property]), '->load() makes a reference to the created ones'); $inner = $services[(string) $property]; - $this->assertEquals('BazClass', $inner->getClass(), '->load() uses the same configuration as for the anonymous ones'); + $this->assertEquals('BuzClass', $inner->getClass(), '->load() uses the same configuration as for the anonymous ones'); + $this->assertFalse($inner->isPublic()); + + // "wild" service + $service = $container->findTaggedServiceIds('biz_tag'); + $this->assertCount(1, $service); + + foreach ($service as $id => $tag) { + $service = $container->getDefinition($id); + } + $this->assertEquals('BizClass', $service->getClass(), '->load() uses the same configuration as for the anonymous ones'); + $this->assertFalse($service->isPublic()); + + // anonymous services are shared when using decoration definitions + $container->compile(); + $services = $container->getDefinitions(); + $fooArgs = $services['foo']->getArguments(); + $barArgs = $services['bar']->getArguments(); + $this->assertSame($fooArgs[0], $barArgs[0]); } /** diff --git a/src/Symfony/Component/DependencyInjection/Variable.php b/src/Symfony/Component/DependencyInjection/Variable.php index e50235607ec8f..ddd43743826f6 100644 --- a/src/Symfony/Component/DependencyInjection/Variable.php +++ b/src/Symfony/Component/DependencyInjection/Variable.php @@ -29,8 +29,6 @@ class Variable private $name; /** - * Constructor. - * * @param string $name */ public function __construct($name) diff --git a/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php b/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php index 3e05015873588..c429417c25f80 100644 --- a/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php +++ b/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php @@ -263,7 +263,8 @@ private function buildOptionValue(\DOMElement $node) { $option = array(); - $defaultValue = (isset($node->nodeValue) && !empty($node->nodeValue)) ? $node->nodeValue : 'on'; + $defaultDefaultValue = 'select' === $this->node->nodeName ? '' : 'on'; + $defaultValue = (isset($node->nodeValue) && !empty($node->nodeValue)) ? $node->nodeValue : $defaultDefaultValue; $option['value'] = $node->hasAttribute('value') ? $node->getAttribute('value') : $defaultValue; $option['disabled'] = $node->hasAttribute('disabled'); diff --git a/src/Symfony/Component/DomCrawler/Tests/Field/ChoiceFormFieldTest.php b/src/Symfony/Component/DomCrawler/Tests/Field/ChoiceFormFieldTest.php index 8b35cc9ac5ba0..9592286305651 100644 --- a/src/Symfony/Component/DomCrawler/Tests/Field/ChoiceFormFieldTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/Field/ChoiceFormFieldTest.php @@ -351,6 +351,14 @@ public function testDisableValidation() $this->assertEquals(array('foobar'), $field->getValue(), '->disableValidation() allows to set a value which is not in the selected options.'); } + public function testSelectWithEmptyValue() + { + $node = $this->createSelectNodeWithEmptyOption(array('' => true, 'Female' => false, 'Male' => false)); + $field = new ChoiceFormField($node); + + $this->assertSame('', $field->getValue()); + } + protected function createSelectNode($options, $attributes = array(), $selectedAttrText = 'selected') { $document = new \DOMDocument(); diff --git a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php index 7653ccfb7175f..12e2b1c67b310 100644 --- a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php +++ b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php @@ -116,6 +116,10 @@ public function dispatch($eventName, Event $event = null) $event = new Event(); } + if (null !== $this->logger && $event->isPropagationStopped()) { + $this->logger->debug(sprintf('The "%s" event is already stopped. No listeners have been called.', $eventName)); + } + $this->preProcess($eventName); $this->preDispatch($eventName, $event); diff --git a/src/Symfony/Component/EventDispatcher/EventDispatcher.php b/src/Symfony/Component/EventDispatcher/EventDispatcher.php index b54d07b03dfb4..f1b63f709dc1c 100644 --- a/src/Symfony/Component/EventDispatcher/EventDispatcher.php +++ b/src/Symfony/Component/EventDispatcher/EventDispatcher.php @@ -155,10 +155,10 @@ public function removeSubscriber(EventSubscriberInterface $subscriber) protected function doDispatch($listeners, $eventName, Event $event) { foreach ($listeners as $listener) { - call_user_func($listener, $event, $eventName, $this); if ($event->isPropagationStopped()) { break; } + call_user_func($listener, $event, $eventName, $this); } } diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php index d8a072f1cc488..640e27f2abda0 100644 --- a/src/Symfony/Component/Filesystem/Filesystem.php +++ b/src/Symfony/Component/Filesystem/Filesystem.php @@ -155,24 +155,27 @@ public function touch($files, $time = null, $atime = null) */ public function remove($files) { - $files = iterator_to_array($this->toIterator($files)); + if ($files instanceof \Traversable) { + $files = iterator_to_array($files, false); + } elseif (!is_array($files)) { + $files = array($files); + } $files = array_reverse($files); foreach ($files as $file) { - if (@(unlink($file) || rmdir($file))) { - continue; - } if (is_link($file)) { // See https://bugs.php.net/52176 - $error = error_get_last(); - throw new IOException(sprintf('Failed to remove symlink "%s": %s.', $file, $error['message'])); + if (!@(unlink($file) || '\\' !== DIRECTORY_SEPARATOR || rmdir($file)) && file_exists($file)) { + $error = error_get_last(); + throw new IOException(sprintf('Failed to remove symlink "%s": %s.', $file, $error['message'])); + } } elseif (is_dir($file)) { - $this->remove(new \FilesystemIterator($file)); + $this->remove(new \FilesystemIterator($file, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS)); - if (!@rmdir($file)) { + if (!@rmdir($file) && file_exists($file)) { $error = error_get_last(); throw new IOException(sprintf('Failed to remove directory "%s": %s.', $file, $error['message'])); } - } elseif (file_exists($file)) { + } elseif (!@unlink($file) && file_exists($file)) { $error = error_get_last(); throw new IOException(sprintf('Failed to remove file "%s": %s.', $file, $error['message'])); } diff --git a/src/Symfony/Component/Form/Extension/Core/DataMapper/CheckboxListMapper.php b/src/Symfony/Component/Form/Extension/Core/DataMapper/CheckboxListMapper.php index b1169ceafa8c3..b85fb0025fe61 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataMapper/CheckboxListMapper.php +++ b/src/Symfony/Component/Form/Extension/Core/DataMapper/CheckboxListMapper.php @@ -11,9 +11,8 @@ namespace Symfony\Component\Form\Extension\Core\DataMapper; -use Symfony\Component\Form\ChoiceList\ChoiceListInterface; use Symfony\Component\Form\DataMapperInterface; -use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Exception\UnexpectedTypeException; /** * Maps choices to/from checkbox forms. @@ -26,16 +25,6 @@ */ class CheckboxListMapper implements DataMapperInterface { - /** - * @var ChoiceListInterface - */ - private $choiceList; - - public function __construct(ChoiceListInterface $choiceList) - { - $this->choiceList = $choiceList; - } - /** * {@inheritdoc} */ @@ -46,22 +35,12 @@ public function mapDataToForms($choices, $checkboxes) } if (!is_array($choices)) { - throw new TransformationFailedException('Expected an array.'); - } - - try { - $valueMap = array_flip($this->choiceList->getValuesForChoices($choices)); - } catch (\Exception $e) { - throw new TransformationFailedException( - 'Can not read the choices from the choice list.', - $e->getCode(), - $e - ); + throw new UnexpectedTypeException($choices, 'array'); } foreach ($checkboxes as $checkbox) { $value = $checkbox->getConfig()->getOption('value'); - $checkbox->setData(isset($valueMap[$value]) ? true : false); + $checkbox->setData(in_array($value, $choices, true)); } } @@ -70,6 +49,10 @@ public function mapDataToForms($choices, $checkboxes) */ public function mapFormsToData($checkboxes, &$choices) { + if (!is_array($choices)) { + throw new UnexpectedTypeException($choices, 'array'); + } + $values = array(); foreach ($checkboxes as $checkbox) { @@ -79,14 +62,6 @@ public function mapFormsToData($checkboxes, &$choices) } } - try { - $choices = $this->choiceList->getChoicesForValues($values); - } catch (\Exception $e) { - throw new TransformationFailedException( - 'Can not read the values from the choice list.', - $e->getCode(), - $e - ); - } + $choices = $values; } } diff --git a/src/Symfony/Component/Form/Extension/Core/DataMapper/RadioListMapper.php b/src/Symfony/Component/Form/Extension/Core/DataMapper/RadioListMapper.php index d08a603b5c90e..6f757c601f9b4 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataMapper/RadioListMapper.php +++ b/src/Symfony/Component/Form/Extension/Core/DataMapper/RadioListMapper.php @@ -11,8 +11,8 @@ namespace Symfony\Component\Form\Extension\Core\DataMapper; -use Symfony\Component\Form\ChoiceList\ChoiceListInterface; use Symfony\Component\Form\DataMapperInterface; +use Symfony\Component\Form\Exception\UnexpectedTypeException; /** * Maps choices to/from radio forms. @@ -25,24 +25,18 @@ */ class RadioListMapper implements DataMapperInterface { - /** - * @var ChoiceListInterface - */ - private $choiceList; - - public function __construct(ChoiceListInterface $choiceList) - { - $this->choiceList = $choiceList; - } - /** * {@inheritdoc} */ - public function mapDataToForms($data, $radios) + public function mapDataToForms($choice, $radios) { + if (!is_string($choice)) { + throw new UnexpectedTypeException($choice, 'string'); + } + foreach ($radios as $radio) { $value = $radio->getConfig()->getOption('value'); - $radio->setData($value === $data ? true : false); + $radio->setData($choice === $value); } } @@ -51,6 +45,10 @@ public function mapDataToForms($data, $radios) */ public function mapFormsToData($radios, &$choice) { + if (null !== $choice && !is_string($choice)) { + throw new UnexpectedTypeException($choice, 'null or string'); + } + $choice = null; foreach ($radios as $radio) { @@ -59,8 +57,7 @@ public function mapFormsToData($radios, &$choice) return; } - $value = $radio->getConfig()->getOption('value'); - $choice = current($this->choiceList->getChoicesForValues(array($value))); + $choice = $radio->getConfig()->getOption('value'); return; } diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php index c0cfdad7e27c5..2880eede342e2 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php @@ -205,6 +205,10 @@ public function reverseTransform($value) throw new TransformationFailedException('I don\'t have a clear idea what infinity looks like'); } + if (is_int($result) && $result === (int) $float = (float) $result) { + $result = $float; + } + if (function_exists('mb_detect_encoding') && false !== $encoding = mb_detect_encoding($value, null, true)) { $length = mb_strlen($value, $encoding); $remainder = mb_substr($value, $position, $length, $encoding); diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index 835f3767e54d9..a61317de8522b 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -60,9 +60,7 @@ public function __construct(ChoiceListFactoryInterface $choiceListFactory = null public function buildForm(FormBuilderInterface $builder, array $options) { if ($options['expanded']) { - $builder->setDataMapper($options['multiple'] - ? new CheckboxListMapper($options['choice_list']) - : new RadioListMapper($options['choice_list'])); + $builder->setDataMapper($options['multiple'] ? new CheckboxListMapper() : new RadioListMapper()); // Initialize all choices before doing the index check below. // This helps in cases where index checks are optimized for non @@ -133,30 +131,13 @@ public function buildForm(FormBuilderInterface $builder, array $options) $event->setData($data); }); + } - if (!$options['multiple']) { - // For radio lists, transform empty arrays to null - // This is kind of a hack necessary because the RadioListMapper - // is not invoked for forms without choices - $builder->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) { - if (array() === $event->getData()) { - $event->setData(null); - } - }); - // For radio lists, pre selection of the choice needs to pre set data - // with the string value so it can be matched in - // {@link \Symfony\Component\Form\Extension\Core\DataMapper\RadioListMapper::mapDataToForms()} - $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { - $choiceList = $event->getForm()->getConfig()->getOption('choice_list'); - $value = current($choiceList->getValuesForChoices(array($event->getData()))); - $event->setData((string) $value); - }); - } - } elseif ($options['multiple']) { - // tag with "multiple" option or list of checkbox inputs $builder->addViewTransformer(new ChoicesToValuesTransformer($options['choice_list'])); } else { - // tag without "multiple" option or list of radio inputs $builder->addViewTransformer(new ChoiceToValueTransformer($options['choice_list'])); } @@ -255,7 +236,11 @@ public function configureOptions(OptionsResolver $resolver) $choiceListFactory = $this->choiceListFactory; $emptyData = function (Options $options) { - if ($options['multiple'] || $options['expanded']) { + if ($options['expanded'] && !$options['multiple']) { + return; + } + + if ($options['multiple']) { return array(); } @@ -420,7 +405,7 @@ public function configureOptions(OptionsResolver $resolver) $resolver->setAllowedTypes('choice_value', array('null', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath')); $resolver->setAllowedTypes('choice_attr', array('null', 'array', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath')); $resolver->setAllowedTypes('preferred_choices', array('array', '\Traversable', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath')); - $resolver->setAllowedTypes('group_by', array('null', 'array', '\Traversable', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath')); + $resolver->setAllowedTypes('group_by', array('null', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath')); } /** @@ -431,21 +416,6 @@ public function getName() return 'choice'; } - private static function flipRecursive($choices, &$output = array()) - { - foreach ($choices as $key => $value) { - if (is_array($value)) { - $output[$key] = array(); - self::flipRecursive($value, $output[$key]); - continue; - } - - $output[$value] = $key; - } - - return $output; - } - /** * Adds the sub fields for an expanded choice field. * @@ -503,14 +473,6 @@ private function addSubForm(FormBuilderInterface $builder, $name, ChoiceView $ch private function createChoiceListView(ChoiceListInterface $choiceList, array $options) { - // If no explicit grouping information is given, use the structural - // information from the "choices" option for creating groups - if (!$options['group_by'] && $options['choices']) { - $options['group_by'] = !$options['choices_as_values'] - ? self::flipRecursive($options['choices']) - : $options['choices']; - } - return $this->choiceListFactory->createView( $choiceList, $options['preferred_choices'], diff --git a/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php b/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php index 8caacb2dc539d..ea86bf6c4b1e2 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php @@ -28,7 +28,6 @@ public function buildForm(FormBuilderInterface $builder, array $options) { if ($options['allow_add'] && $options['prototype']) { $prototype = $builder->create($options['prototype_name'], $options['type'], array_replace(array( - 'required' => $options['required'], 'label' => $options['prototype_name'].'label__', ), $options['options'])); $builder->setAttribute('prototype', $prototype->getForm()); @@ -56,7 +55,8 @@ public function buildView(FormView $view, FormInterface $form, array $options) )); if ($form->getConfig()->hasAttribute('prototype')) { - $view->vars['prototype'] = $form->getConfig()->getAttribute('prototype')->createView($view); + $prototype = $form->getConfig()->getAttribute('prototype'); + $view->vars['prototype'] = $prototype->setParent($form)->createView($view); } } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php index 24fa3e5424615..c500d1552c7be 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php @@ -203,7 +203,7 @@ public function buildView(FormView $view, FormInterface $form, array $options) public function configureOptions(OptionsResolver $resolver) { $compound = function (Options $options) { - return $options['widget'] !== 'single_text'; + return 'single_text' !== $options['widget']; }; // Defaults to the value of "widget" diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php index b1eb4382ed3ec..ae4c6bacf2066 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php @@ -78,7 +78,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) $pattern ); - // new \intlDateFormatter may return null instead of false in case of failure, see https://bugs.php.net/bug.php?id=66323 + // new \IntlDateFormatter may return null instead of false in case of failure, see https://bugs.php.net/bug.php?id=66323 if (!$formatter) { throw new InvalidOptionsException(intl_get_error_message(), intl_get_error_code()); } @@ -175,7 +175,7 @@ public function finishView(FormView $view, FormInterface $form, array $options) public function configureOptions(OptionsResolver $resolver) { $compound = function (Options $options) { - return $options['widget'] !== 'single_text'; + return 'single_text' !== $options['widget']; }; $placeholder = $placeholderDefault = function (Options $options) { @@ -206,7 +206,7 @@ public function configureOptions(OptionsResolver $resolver) }; $format = function (Options $options) { - return $options['widget'] === 'single_text' ? DateType::HTML5_FORMAT : DateType::DEFAULT_FORMAT; + return 'single_text' === $options['widget'] ? DateType::HTML5_FORMAT : DateType::DEFAULT_FORMAT; }; $resolver->setDefaults(array( diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php index 57c9a44c8f7e5..8232170278502 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php @@ -163,7 +163,7 @@ public function buildView(FormView $view, FormInterface $form, array $options) public function configureOptions(OptionsResolver $resolver) { $compound = function (Options $options) { - return $options['widget'] !== 'single_text'; + return 'single_text' !== $options['widget']; }; $placeholder = $placeholderDefault = function (Options $options) { diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php index 0f68dc851a58f..47c38c5718b06 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -766,11 +766,7 @@ public function isValid() return true; } - if (count($this->getErrors(true)) > 0) { - return false; - } - - return true; + return 0 === count($this->getErrors(true)); } /** diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php index 7f8626bdbdd38..29b9afef831b4 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php @@ -642,10 +642,17 @@ public function testReverseTransformDisallowsTrailingExtraCharactersMultibyte() $transformer->reverseTransform("12\xc2\xa0345,678foo"); } - public function testReverseTransformBigint() + public function testReverseTransformBigInt() { $transformer = new NumberToLocalizedStringTransformer(null, true); $this->assertEquals(PHP_INT_MAX - 1, (int) $transformer->reverseTransform((string) (PHP_INT_MAX - 1))); } + + public function testReverseTransformSmallInt() + { + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $this->assertSame(1.0, $transformer->reverseTransform('1')); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php index 9dc6bfd6b55b8..a00fcacdb70be 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php @@ -32,6 +32,12 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'n/a' => '', ); + private $booleanChoicesWithNull = array( + 'Yes' => true, + 'No' => false, + 'n/a' => null, + ); + private $numericChoicesFlipped = array( 0 => 'Bernhard', 1 => 'Fabien', @@ -183,6 +189,19 @@ public function testExpandedChoiceListWithScalarValues() $this->assertTrue($view->children[2]->vars['checked'], 'Empty value should be pre selected'); } + public function testExpandedChoiceListWithBooleanAndNullValues() + { + $view = $this->factory->create('choice', null, array( + 'choices' => $this->booleanChoicesWithNull, + 'choices_as_values' => true, + 'expanded' => true, + ))->createView(); + + $this->assertFalse($view->children[0]->vars['checked'], 'True value should not be pre selected'); + $this->assertFalse($view->children[1]->vars['checked'], 'False value should not be pre selected'); + $this->assertTrue($view->children[2]->vars['checked'], 'Empty value should be pre selected'); + } + public function testExpandedChoiceListWithScalarValuesAndFalseAsPreSetData() { $view = $this->factory->create('choice', false, array( @@ -197,6 +216,19 @@ public function testExpandedChoiceListWithScalarValuesAndFalseAsPreSetData() $this->assertFalse($view->children[2]->vars['checked'], 'Empty value should not be pre selected'); } + public function testExpandedChoiceListWithBooleanAndNullValuesAndFalseAsPreSetData() + { + $view = $this->factory->create('choice', false, array( + 'choices' => $this->booleanChoicesWithNull, + 'choices_as_values' => true, + 'expanded' => true, + ))->createView(); + + $this->assertFalse($view->children[0]->vars['checked'], 'True value should not be pre selected'); + $this->assertTrue($view->children[1]->vars['checked'], 'False value should be pre selected'); + $this->assertFalse($view->children[2]->vars['checked'], 'Null value should not be pre selected'); + } + public function testPlaceholderPresentOnNonRequiredExpandedSingleChoice() { $form = $this->factory->create('choice', null, array( @@ -319,7 +351,7 @@ public function testPlaceholderWithExpandedBooleanChoices() $view = $form->createView(); - $this->assertSame('', $view->vars['value'], 'Value should be empty'); + $this->assertSame('', $view->vars['value'], 'Value should be an empty string'); $this->assertSame('1', $view->vars['choices'][0]->value); $this->assertSame('0', $view->vars['choices'][1]->value, 'Choice "false" should have "0" as value'); $this->assertFalse($view->children[1]->vars['checked'], 'Choice "false" should not be selected'); @@ -940,8 +972,8 @@ public function testSubmitSingleExpandedRequiredNull() $form->submit(null); $this->assertNull($form->getData()); - $this->assertNull($form->getViewData()); - $this->assertEmpty($form->getExtraData()); + $this->assertSame('', $form->getViewData(), 'View data should always be a string'); + $this->assertSame(array(), $form->getExtraData(), 'ChoiceType is compound when expanded, extra data should always be an array'); $this->assertTrue($form->isSynchronized()); $this->assertFalse($form[0]->getData()); @@ -972,8 +1004,8 @@ public function testSubmitSingleExpandedRequiredNullNoChoices() $form->submit(null); $this->assertNull($form->getData()); - $this->assertNull($form->getViewData()); - $this->assertEmpty($form->getExtraData()); + $this->assertSame('', $form->getViewData(), 'View data should always be a string'); + $this->assertSame(array(), $form->getExtraData(), 'ChoiceType is compound when expanded, extra data should always be an array'); $this->assertTrue($form->isSynchronized()); } @@ -990,8 +1022,8 @@ public function testSubmitSingleExpandedRequiredEmpty() $form->submit(''); $this->assertNull($form->getData()); - $this->assertNull($form->getViewData()); - $this->assertEmpty($form->getExtraData()); + $this->assertSame('', $form->getViewData(), 'View data should always be a string'); + $this->assertSame(array(), $form->getExtraData(), 'ChoiceType is compound when expanded, extra data should always be an array'); $this->assertTrue($form->isSynchronized()); $this->assertFalse($form[0]->getData()); @@ -1022,8 +1054,8 @@ public function testSubmitSingleExpandedRequiredEmptyNoChoices() $form->submit(''); $this->assertNull($form->getData()); - $this->assertNull($form->getViewData()); - $this->assertEmpty($form->getExtraData()); + $this->assertSame('', $form->getViewData(), 'View data should always be a string'); + $this->assertSame(array(), $form->getExtraData(), 'ChoiceType is compound when expanded, extra data should always be an array'); $this->assertTrue($form->isSynchronized()); } @@ -1040,8 +1072,8 @@ public function testSubmitSingleExpandedRequiredFalse() $form->submit(false); $this->assertNull($form->getData()); - $this->assertNull($form->getViewData()); - $this->assertEmpty($form->getExtraData()); + $this->assertSame('', $form->getViewData(), 'View data should always be a string'); + $this->assertSame(array(), $form->getExtraData(), 'ChoiceType is compound when expanded, extra data should always be an array'); $this->assertTrue($form->isSynchronized()); $this->assertFalse($form[0]->getData()); @@ -1072,8 +1104,8 @@ public function testSubmitSingleExpandedRequiredFalseNoChoices() $form->submit(false); $this->assertNull($form->getData()); - $this->assertNull($form->getViewData()); - $this->assertEmpty($form->getExtraData()); + $this->assertSame('', $form->getViewData(), 'View data should always be a string'); + $this->assertSame(array(), $form->getExtraData(), 'ChoiceType is compound when expanded, extra data should always be an array'); $this->assertTrue($form->isSynchronized()); } @@ -1090,8 +1122,8 @@ public function testSubmitSingleExpandedNonRequiredNull() $form->submit(null); $this->assertNull($form->getData()); - $this->assertNull($form->getViewData()); - $this->assertEmpty($form->getExtraData()); + $this->assertSame('', $form->getViewData(), 'View data should always be a string'); + $this->assertSame(array(), $form->getExtraData(), 'ChoiceType is compound when expanded, extra data should always be an array'); $this->assertTrue($form->isSynchronized()); $this->assertTrue($form['placeholder']->getData()); @@ -1124,8 +1156,8 @@ public function testSubmitSingleExpandedNonRequiredNullNoChoices() $form->submit(null); $this->assertNull($form->getData()); - $this->assertNull($form->getViewData()); - $this->assertEmpty($form->getExtraData()); + $this->assertSame('', $form->getViewData(), 'View data should always be a string'); + $this->assertSame(array(), $form->getExtraData(), 'ChoiceType is compound when expanded, extra data should always be an array'); $this->assertTrue($form->isSynchronized()); } @@ -1142,8 +1174,8 @@ public function testSubmitSingleExpandedNonRequiredEmpty() $form->submit(''); $this->assertNull($form->getData()); - $this->assertNull($form->getViewData()); - $this->assertEmpty($form->getExtraData()); + $this->assertSame('', $form->getViewData(), 'View data should always be a string'); + $this->assertSame(array(), $form->getExtraData(), 'ChoiceType is compound when expanded, extra data should always be an array'); $this->assertTrue($form->isSynchronized()); $this->assertTrue($form['placeholder']->getData()); @@ -1176,8 +1208,8 @@ public function testSubmitSingleExpandedNonRequiredEmptyNoChoices() $form->submit(''); $this->assertNull($form->getData()); - $this->assertNull($form->getViewData()); - $this->assertEmpty($form->getExtraData()); + $this->assertSame('', $form->getViewData(), 'View data should always be a string'); + $this->assertSame(array(), $form->getExtraData(), 'ChoiceType is compound when expanded, extra data should always be an array'); $this->assertTrue($form->isSynchronized()); } @@ -1194,8 +1226,8 @@ public function testSubmitSingleExpandedNonRequiredFalse() $form->submit(false); $this->assertNull($form->getData()); - $this->assertNull($form->getViewData()); - $this->assertEmpty($form->getExtraData()); + $this->assertSame('', $form->getViewData(), 'View data should always be a string'); + $this->assertSame(array(), $form->getExtraData(), 'ChoiceType is compound when expanded, extra data should always be an array'); $this->assertTrue($form->isSynchronized()); $this->assertTrue($form['placeholder']->getData()); @@ -1228,8 +1260,8 @@ public function testSubmitSingleExpandedNonRequiredFalseNoChoices() $form->submit(false); $this->assertNull($form->getData()); - $this->assertNull($form->getViewData()); - $this->assertEmpty($form->getExtraData()); + $this->assertSame('', $form->getViewData(), 'View data should always be a string'); + $this->assertSame(array(), $form->getExtraData(), 'ChoiceType is compound when expanded, extra data should always be an array'); $this->assertTrue($form->isSynchronized()); } @@ -1247,7 +1279,7 @@ public function testSubmitSingleExpandedWithEmptyChild() $form->submit(''); - $this->assertNull($form->getData()); + $this->assertSame('', $form->getData()); $this->assertTrue($form->isSynchronized()); $this->assertTrue($form[0]->getData()); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php index efe241d84d4c9..fe4543e8442fa 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php @@ -300,4 +300,46 @@ public function testPrototypeSetNotRequired() $this->assertFalse($form->createView()->vars['required'], 'collection is not required'); $this->assertFalse($form->createView()->vars['prototype']->vars['required'], '"prototype" should not be required'); } + + public function testPrototypeSetNotRequiredIfParentNotRequired() + { + $child = $this->factory->create('collection', array(), array( + 'type' => 'file', + 'allow_add' => true, + 'prototype' => true, + 'prototype_name' => '__test__', + )); + + $parent = $this->factory->create('form', array(), array( + 'required' => false, + )); + + $child->setParent($parent); + $this->assertFalse($parent->createView()->vars['required'], 'Parent is not required'); + $this->assertFalse($child->createView()->vars['required'], 'Child is not required'); + $this->assertFalse($child->createView()->vars['prototype']->vars['required'], '"Prototype" should not be required'); + } + + public function testPrototypeNotOverrideRequiredByEntryOptionsInFavorOfParent() + { + $child = $this->factory->create('collection', array(), array( + 'type' => 'file', + 'allow_add' => true, + 'prototype' => true, + 'prototype_name' => '__test__', + 'options' => array( + 'required' => true, + ), + )); + + $parent = $this->factory->create('form', array(), array( + 'required' => false, + )); + + $child->setParent($parent); + + $this->assertFalse($parent->createView()->vars['required'], 'Parent is not required'); + $this->assertFalse($child->createView()->vars['required'], 'Child is not required'); + $this->assertFalse($child->createView()->vars['prototype']->vars['required'], '"Prototype" should not be required'); + } } diff --git a/src/Symfony/Component/HttpFoundation/File/UploadedFile.php b/src/Symfony/Component/HttpFoundation/File/UploadedFile.php index 6b869e0ab4c29..ace70db905340 100644 --- a/src/Symfony/Component/HttpFoundation/File/UploadedFile.php +++ b/src/Symfony/Component/HttpFoundation/File/UploadedFile.php @@ -50,7 +50,7 @@ class UploadedFile extends File /** * The file size provided by the uploader. * - * @var string + * @var int|null */ private $size; @@ -75,12 +75,12 @@ class UploadedFile extends File * * Calling any other method on an non-valid instance will cause an unpredictable result. * - * @param string $path The full temporary path to the file - * @param string $originalName The original file name - * @param string $mimeType The type of the file as provided by PHP - * @param int $size The file size - * @param int $error The error constant of the upload (one of PHP's UPLOAD_ERR_XXX constants) - * @param bool $test Whether the test mode is active + * @param string $path The full temporary path to the file + * @param string $originalName The original file name + * @param string|null $mimeType The type of the file as provided by PHP; null defaults to application/octet-stream + * @param int|null $size The file size + * @param int|null $error The error constant of the upload (one of PHP's UPLOAD_ERR_XXX constants); null defaults to UPLOAD_ERR_OK + * @param bool $test Whether the test mode is active * * @throws FileException If file_uploads is disabled * @throws FileNotFoundException If the file does not exist diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index b5b7c91a2c817..da6244ddb917c 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -715,7 +715,7 @@ public static function getHttpMethodParameterOverride() * public property instead (query, attributes, request). * * @param string $key the key - * @param mixed $default the default value + * @param mixed $default the default value if the parameter key does not exist * @param bool $deep is parameter deep in multidimensional array * * @return mixed diff --git a/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php b/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php index fc2efed86bd6a..1a90f1ebe7720 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php @@ -49,7 +49,7 @@ public function onKernelException(GetResponseForExceptionEvent $event) try { $response = $event->getKernel()->handle($request, HttpKernelInterface::SUB_REQUEST, false); } catch (\Exception $e) { - $this->logException($e, sprintf('Exception thrown when handling an exception (%s: %s at %s line %s)', get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()), false); + $this->logException($e, sprintf('Exception thrown when handling an exception (%s: %s at %s line %s)', get_class($e), $e->getMessage(), $e->getFile(), $e->getLine())); $wrapper = $e; diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 9693fb7f0b1ef..bee3360413739 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -58,11 +58,11 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.7.11'; - const VERSION_ID = 20711; + const VERSION = '2.7.12'; + const VERSION_ID = 20712; const MAJOR_VERSION = 2; const MINOR_VERSION = 7; - const RELEASE_VERSION = 11; + const RELEASE_VERSION = 12; const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '05/2018'; diff --git a/src/Symfony/Component/Process/Pipes/AbstractPipes.php b/src/Symfony/Component/Process/Pipes/AbstractPipes.php index f2fd35eb43c20..ffa6a88319fb9 100644 --- a/src/Symfony/Component/Process/Pipes/AbstractPipes.php +++ b/src/Symfony/Component/Process/Pipes/AbstractPipes.php @@ -22,10 +22,9 @@ abstract class AbstractPipes implements PipesInterface public $pipes = array(); /** @var string */ - protected $inputBuffer = ''; + private $inputBuffer = ''; /** @var resource|null */ - protected $input; - + private $input; /** @var bool */ private $blocked = true; @@ -91,9 +90,8 @@ protected function write() if (!isset($this->pipes[0])) { return; } - - $e = array(); - $r = null !== $this->input ? array($this->input) : $e; + $input = $this->input; + $r = $e = array(); $w = array($this->pipes[0]); // let's have a look if something changed in streams @@ -110,7 +108,7 @@ protected function write() } } - foreach ($r as $input) { + if ($input) { for (;;) { $data = fread($input, self::CHUNK_SIZE); if (!isset($data[0])) { @@ -124,7 +122,7 @@ protected function write() return array($this->pipes[0]); } } - if (!isset($data[0]) && feof($input)) { + if (feof($input)) { // no more data to read on input resource // use an empty buffer in the next reads $this->input = null; diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index 8047ef971dd32..08742df2d9c85 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -158,7 +158,7 @@ public function __construct($commandline, $cwd = null, array $env = null, $input $this->setEnv($env); } - $this->input = $input; + $this->setInput($input); $this->setTimeout($timeout); $this->useFileHandles = '\\' === DIRECTORY_SEPARATOR; $this->pty = false; @@ -1087,7 +1087,7 @@ public function setInput($input) throw new LogicException('Input can not be set while the process is running.'); } - $this->input = ProcessUtils::validateInput(sprintf('%s::%s', __CLASS__, __FUNCTION__), $input); + $this->input = ProcessUtils::validateInput(__METHOD__, $input); return $this; } diff --git a/src/Symfony/Component/Process/ProcessBuilder.php b/src/Symfony/Component/Process/ProcessBuilder.php index a782fd69e94dd..0f4764638a3e9 100644 --- a/src/Symfony/Component/Process/ProcessBuilder.php +++ b/src/Symfony/Component/Process/ProcessBuilder.php @@ -177,7 +177,7 @@ public function addEnvironmentVariables(array $variables) */ public function setInput($input) { - $this->input = ProcessUtils::validateInput(sprintf('%s::%s', __CLASS__, __FUNCTION__), $input); + $this->input = ProcessUtils::validateInput(__METHOD__, $input); return $this; } diff --git a/src/Symfony/Component/Process/ProcessUtils.php b/src/Symfony/Component/Process/ProcessUtils.php index 4f30b630dcfe5..0bd2f6b772f6f 100644 --- a/src/Symfony/Component/Process/ProcessUtils.php +++ b/src/Symfony/Component/Process/ProcessUtils.php @@ -80,7 +80,7 @@ public static function escapeArgument($argument) * @param string $caller The name of method call that validates the input * @param mixed $input The input to validate * - * @return string The validated input + * @return mixed The validated input * * @throws InvalidArgumentException In case the input is not valid * @@ -92,6 +92,9 @@ public static function validateInput($caller, $input) if (is_resource($input)) { return $input; } + if (is_string($input)) { + return $input; + } if (is_scalar($input)) { return (string) $input; } diff --git a/src/Symfony/Component/Process/Tests/ProcessTest.php b/src/Symfony/Component/Process/Tests/ProcessTest.php index d1496236a3101..bb7914603ebab 100644 --- a/src/Symfony/Component/Process/Tests/ProcessTest.php +++ b/src/Symfony/Component/Process/Tests/ProcessTest.php @@ -209,6 +209,24 @@ public function testSetStreamAsInput($code, $size) $this->assertEquals($expectedLength, strlen($p->getErrorOutput())); } + public function testLiveStreamAsInput() + { + $stream = fopen('php://memory', 'r+'); + fwrite($stream, 'hello'); + rewind($stream); + + $p = $this->getProcess(sprintf('%s -r %s', self::$phpBin, escapeshellarg('stream_copy_to_stream(STDIN, STDOUT);'))); + $p->setInput($stream); + $p->start(function ($type, $data) use ($stream) { + if ('hello' === $data) { + fclose($stream); + } + }); + $p->wait(); + + $this->assertSame('hello', $p->getOutput()); + } + /** * @expectedException \Symfony\Component\Process\Exception\LogicException * @expectedExceptionMessage Input can not be set while the process is running. @@ -1163,7 +1181,7 @@ public function pipesCodeProvider() * @dataProvider provideVariousIncrementals */ public function testIncrementalOutputDoesNotRequireAnotherCall($stream, $method) { - $process = new Process(self::$phpBin.' -r '.escapeshellarg('$n = 0; while ($n < 3) { file_put_contents(\''.$stream.'\', $n, 1); $n++; usleep(1000); }'), null, null, null, null); + $process = $this->getProcess(self::$phpBin.' -r '.escapeshellarg('$n = 0; while ($n < 3) { file_put_contents(\''.$stream.'\', $n, 1); $n++; usleep(1000); }'), null, null, null, null); $process->start(); $result = ''; $limit = microtime(true) + 3; @@ -1184,24 +1202,6 @@ public function provideVariousIncrementals() { ); } - /** - * provides default method names for simple getter/setter. - */ - public function methodProvider() - { - $defaults = array( - array('CommandLine'), - array('Timeout'), - array('WorkingDirectory'), - array('Env'), - array('Stdin'), - array('Input'), - array('Options'), - ); - - return $defaults; - } - /** * @param string $commandline * @param null|string $cwd diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 1796a6c1a2892..a1ff632231dd2 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -191,6 +191,7 @@ public function setValue(&$objectOrArray, $propertyPath, $value) if ($propertyPath->isIndex($i)) { if ($overwrite = !isset($zval[self::REF])) { $ref = &$zval[self::REF]; + $ref = $zval[self::VALUE]; } $this->writeIndex($zval, $property, $value); if ($overwrite) { @@ -372,10 +373,11 @@ private function readPropertiesUntil($zval, PropertyPathInterface $propertyPath, } if ($i + 1 < $propertyPath->getLength()) { - $zval[self::VALUE][$property] = array(); - if (isset($zval[self::REF])) { + $zval[self::VALUE][$property] = array(); $zval[self::REF] = $zval[self::VALUE]; + } else { + $zval[self::VALUE] = array($property => array()); } } } diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php index 33c86df14ab0b..f3c7b405c1758 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -126,6 +126,15 @@ public function testGetValueReadsMagicGet() $this->assertSame('Bernhard', $this->propertyAccessor->getValue(new TestClassMagicGet('Bernhard'), 'magicProperty')); } + public function testGetValueReadsArrayWithMissingIndexForCustomPropertyPath() + { + $object = new \ArrayObject(); + $array = array('child' => array('index' => $object)); + + $this->assertNull($this->propertyAccessor->getValue($array, '[child][index][foo][bar]')); + $this->assertSame(array(), $object->getArrayCopy()); + } + // https://github.com/symfony/symfony/pull/4450 public function testGetValueReadsMagicGetThatReturnsConstant() { @@ -529,4 +538,14 @@ public function testSetTypeHint() $this->propertyAccessor->setValue($object, 'date', $date); $this->assertSame($date, $object->getDate()); } + + public function testArrayNotBeeingOverwritten() + { + $value = array('value1' => 'foo', 'value2' => 'bar'); + $object = new TestClass($value); + + $this->propertyAccessor->setValue($object, 'publicAccessor[value2]', 'baz'); + $this->assertSame('baz', $this->propertyAccessor->getValue($object, 'publicAccessor[value2]')); + $this->assertSame(array('value1' => 'foo', 'value2' => 'baz'), $object->getPublicAccessor()); + } } diff --git a/src/Symfony/Component/Routing/Generator/UrlGenerator.php b/src/Symfony/Component/Routing/Generator/UrlGenerator.php index 6931bad09d598..e72b6cf974c74 100644 --- a/src/Symfony/Component/Routing/Generator/UrlGenerator.php +++ b/src/Symfony/Component/Routing/Generator/UrlGenerator.php @@ -264,7 +264,10 @@ protected function doGenerate($variables, $defaults, $requirements, $tokens, $pa } // add a query string if needed - $extra = array_diff_key($parameters, $variables, $defaults); + $extra = array_udiff_assoc(array_diff_key($parameters, $variables), $defaults, function ($a, $b) { + return $a == $b ? 0 : 1; + }); + if ($extra && $query = http_build_query($extra, '', '&')) { // "/" and "?" can be left decoded for better user experience, see // http://tools.ietf.org/html/rfc3986#section-3.4 diff --git a/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php b/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php index 120a3b0f4549b..b2a4ecd72db73 100644 --- a/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php +++ b/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php @@ -297,10 +297,22 @@ public function testNullForOptionalParameterIsIgnored() public function testQueryParamSameAsDefault() { - $routes = $this->getRoutes('test', new Route('/test', array('default' => 'value'))); + $routes = $this->getRoutes('test', new Route('/test', array('page' => 1))); - $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test', array('default' => 'foo'))); - $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test', array('default' => 'value'))); + $this->assertSame('/app.php/test?page=2', $this->getGenerator($routes)->generate('test', array('page' => 2))); + $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test', array('page' => 1))); + $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test', array('page' => '1'))); + $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test')); + } + + public function testArrayQueryParamSameAsDefault() + { + $routes = $this->getRoutes('test', new Route('/test', array('array' => array('foo', 'bar')))); + + $this->assertSame('/app.php/test?array%5B0%5D=bar&array%5B1%5D=foo', $this->getGenerator($routes)->generate('test', array('array' => array('bar', 'foo')))); + $this->assertSame('/app.php/test?array%5Ba%5D=foo&array%5Bb%5D=bar', $this->getGenerator($routes)->generate('test', array('array' => array('a' => 'foo', 'b' => 'bar')))); + $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test', array('array' => array('foo', 'bar')))); + $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test', array('array' => array(1 => 'bar', 0 => 'foo')))); $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test')); } diff --git a/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php b/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php index 7c068fe49b933..7de83d2513369 100644 --- a/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Security\Http\Firewall; use Symfony\Component\Security\Core\Exception\AccessDeniedException; +use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; @@ -161,7 +162,7 @@ private function attemptExitUser(Request $request) throw new AuthenticationCredentialsNotFoundException('Could not find original Token object.'); } - if (null !== $this->dispatcher) { + if (null !== $this->dispatcher && $original->getUser() instanceof UserInterface) { $user = $this->provider->refreshUser($original->getUser()); $switchEvent = new SwitchUserEvent($request, $user); $this->dispatcher->dispatch(SecurityEvents::SWITCH_USER, $switchEvent); diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php index f43b564322a72..28d73e0c3b217 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php @@ -158,6 +158,59 @@ public function testExitUserDispatchesEventWithRefreshedUser() $listener->handle($this->event); } + public function testExitUserDoesNotDispatchEventWithStringUser() + { + $originalUser = 'anon.'; + $this + ->userProvider + ->expects($this->never()) + ->method('refreshUser'); + $originalToken = $this->getToken(); + $originalToken + ->expects($this->any()) + ->method('getUser') + ->willReturn($originalUser); + $role = $this + ->getMockBuilder('Symfony\Component\Security\Core\Role\SwitchUserRole') + ->disableOriginalConstructor() + ->getMock(); + $role + ->expects($this->any()) + ->method('getSource') + ->willReturn($originalToken); + $this + ->tokenStorage + ->expects($this->any()) + ->method('getToken') + ->willReturn($this->getToken(array($role))); + $this + ->request + ->expects($this->any()) + ->method('get') + ->with('_switch_user') + ->willReturn('_exit'); + $this + ->request + ->query + ->expects($this->any()) + ->method('all') + ->will($this->returnValue(array())); + $this + ->request + ->expects($this->any()) + ->method('getUri') + ->willReturn('/'); + + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $dispatcher + ->expects($this->never()) + ->method('dispatch') + ; + + $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager, null, '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', $dispatcher); + $listener->handle($this->event); + } + /** * @expectedException \Symfony\Component\Security\Core\Exception\AccessDeniedException */ diff --git a/src/Symfony/Component/Serializer/Serializer.php b/src/Symfony/Component/Serializer/Serializer.php index 59943550a554a..0259bfeda6441 100644 --- a/src/Symfony/Component/Serializer/Serializer.php +++ b/src/Symfony/Component/Serializer/Serializer.php @@ -250,15 +250,6 @@ private function denormalizeObject($data, $class, $format, array $context = arra return $normalizer->denormalize($data, $class, $format, $context); } - foreach ($this->normalizers as $normalizer) { - if ( - $normalizer instanceof DenormalizerInterface && - $normalizer->supportsDenormalization($data, $class, $format) - ) { - return $normalizer->denormalize($data, $class, $format, $context); - } - } - throw new UnexpectedValueException(sprintf('Could not denormalize object of type %s, no supporting normalizer found.', $class)); } diff --git a/src/Symfony/Component/Validator/Constraints/Callback.php b/src/Symfony/Component/Validator/Constraints/Callback.php index bef94dd9cb104..7e4ccd47b540c 100644 --- a/src/Symfony/Component/Validator/Constraints/Callback.php +++ b/src/Symfony/Component/Validator/Constraints/Callback.php @@ -49,7 +49,7 @@ public function __construct($options = null) @trigger_error('The "methods" option of the '.__CLASS__.' class is deprecated since version 2.4 and will be removed in 3.0. Use the "callback" option instead.', E_USER_DEPRECATED); } - if (is_array($options) && !isset($options['callback']) && !isset($options['methods']) && !isset($options['groups'])) { + if (is_array($options) && !isset($options['callback']) && !isset($options['methods']) && !isset($options['groups']) && !isset($options['payload'])) { if (is_callable($options) || !$options) { $options = array('callback' => $options); } else { diff --git a/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php index 1fd348bfb14da..cfa0e99c92325 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php @@ -110,6 +110,7 @@ public function testStrict() /** * @dataProvider getDnsChecks + * @requires function Symfony\Bridge\PhpUnit\DnsMock::withMockedHosts */ public function testDnsChecks($type, $violation) { @@ -144,6 +145,9 @@ public function getDnsChecks() ); } + /** + * @requires function Symfony\Bridge\PhpUnit\DnsMock::withMockedHosts + */ public function testHostnameIsProperlyParsed() { DnsMock::withMockedHosts(array('baz.com' => array(array('type' => 'MX')))); diff --git a/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php index fab7cf18675b5..62d53af8a09a1 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php @@ -194,6 +194,7 @@ public function getValidCustomUrls() /** * @dataProvider getCheckDns + * @requires function Symfony\Bridge\PhpUnit\DnsMock::withMockedHosts */ public function testCheckDns($violation) { diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/Entity.php b/src/Symfony/Component/Validator/Tests/Fixtures/Entity.php index 526ecc5f5c635..b5e9e0c982d7d 100644 --- a/src/Symfony/Component/Validator/Tests/Fixtures/Entity.php +++ b/src/Symfony/Component/Validator/Tests/Fixtures/Entity.php @@ -85,7 +85,7 @@ public function getData() } /** - * @Assert\Callback + * @Assert\Callback(payload="foo") */ public function validateMe(ExecutionContextInterface $context) { diff --git a/src/Symfony/Component/Validator/Tests/Mapping/Loader/AnnotationLoaderTest.php b/src/Symfony/Component/Validator/Tests/Mapping/Loader/AnnotationLoaderTest.php index 75ab3a7bbb5b8..0e2ad41d697a0 100644 --- a/src/Symfony/Component/Validator/Tests/Mapping/Loader/AnnotationLoaderTest.php +++ b/src/Symfony/Component/Validator/Tests/Mapping/Loader/AnnotationLoaderTest.php @@ -53,7 +53,7 @@ public function testLoadClassMetadata() $expected->setGroupSequence(array('Foo', 'Entity')); $expected->addConstraint(new ConstraintA()); $expected->addConstraint(new Callback(array('Symfony\Component\Validator\Tests\Fixtures\CallbackClass', 'callback'))); - $expected->addConstraint(new Callback('validateMe')); + $expected->addConstraint(new Callback(array('callback' => 'validateMe', 'payload' => 'foo'))); $expected->addConstraint(new Callback('validateMeStatic')); $expected->addPropertyConstraint('firstName', new NotNull()); $expected->addPropertyConstraint('firstName', new Range(array('min' => 3))); @@ -123,7 +123,7 @@ public function testLoadClassMetadataAndMerge() $expected->setGroupSequence(array('Foo', 'Entity')); $expected->addConstraint(new ConstraintA()); $expected->addConstraint(new Callback(array('Symfony\Component\Validator\Tests\Fixtures\CallbackClass', 'callback'))); - $expected->addConstraint(new Callback('validateMe')); + $expected->addConstraint(new Callback(array('callback' => 'validateMe', 'payload' => 'foo'))); $expected->addConstraint(new Callback('validateMeStatic')); $expected->addPropertyConstraint('firstName', new NotNull()); $expected->addPropertyConstraint('firstName', new Range(array('min' => 3))); diff --git a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php index cacd2113d7694..94c1bd3a6b8a0 100644 --- a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php @@ -171,12 +171,11 @@ public static function castParameter(\ReflectionParameter $c, array $a, Stub $st if ($c->hasType()) { $a[$prefix.'typeHint'] = $c->getType()->__toString(); } - } elseif ($c->isArray()) { - $a[$prefix.'typeHint'] = 'array'; - } elseif (method_exists($c, 'isCallable') && $c->isCallable()) { - $a[$prefix.'typeHint'] = 'callable'; - } elseif ($v = $c->getClass()) { - $a[$prefix.'typeHint'] = $v->name; + } else { + $v = explode(' ', $c->__toString(), 6); + if (isset($v[5]) && 0 === strspn($v[4], '.&$')) { + $a[$prefix.'typeHint'] = $v[4]; + } } } catch (\ReflectionException $e) { if (preg_match('/^Class ([^ ]++) does not exist$/', $e->getMessage(), $m)) { diff --git a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php index cbdef90dd4a68..5e8a59cce91bd 100644 --- a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php @@ -58,8 +58,8 @@ public function __construct($output = null, $charset = null) { parent::__construct($output, $charset); - if ('\\' === DIRECTORY_SEPARATOR && false !== @getenv('ANSICON')) { - // Use only the base 16 xterm colors when using ANSICON + if ('\\' === DIRECTORY_SEPARATOR && 'ON' !== @getenv('ConEmuANSI') && 'xterm' !== @getenv('TERM')) { + // Use only the base 16 xterm colors when using ANSICON or standard Windows 10 CLI $this->setStyles(array( 'default' => '31', 'num' => '1;34', @@ -448,7 +448,12 @@ protected function supportsColors() } if ('\\' === DIRECTORY_SEPARATOR) { - static::$defaultColors = @(false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM')); + static::$defaultColors = @( + 0 >= version_compare('10.0.10586', PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD) + || false !== getenv('ANSICON') + || 'ON' === getenv('ConEmuANSI') + || 'xterm' === getenv('TERM') + ); } elseif (function_exists('posix_isatty')) { $h = stream_get_meta_data($this->outputStream) + array('wrapper_type' => null); $h = 'Output' === $h['stream_type'] && 'PHP' === $h['wrapper_type'] ? fopen('php://stdout', 'wb') : $this->outputStream; diff --git a/src/Symfony/Component/VarDumper/README.md b/src/Symfony/Component/VarDumper/README.md index 78ba1b29e884b..3b5d55f5f5bfd 100644 --- a/src/Symfony/Component/VarDumper/README.md +++ b/src/Symfony/Component/VarDumper/README.md @@ -2,7 +2,7 @@ VarDumper Component =================== The VarDumper component provides mechanisms for walking through any arbitrary -PHP variable. Built on top, it provides a better `dump()`` function that you +PHP variable. Built on top, it provides a better `dump()` function that you can use instead of `var_dump`. Resources diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php index 5c306a671085c..e98642414f0f7 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\VarDumper\Tests\Caster; use Symfony\Component\VarDumper\Test\VarDumperTestCase; +use Symfony\Component\VarDumper\Tests\Fixtures\NotLoadableClass; /** * @author Nicolas Grekas @@ -47,7 +48,7 @@ public function testReflectionCaster() "export" => ReflectionMethod { +name: "export" +class: "ReflectionClass" - parameters: array:2 [ +%A parameters: array:2 [ "$%s" => ReflectionParameter { %A position: 0 %A } @@ -70,7 +71,7 @@ public function testReflectionParameter() ReflectionParameter { +name: "arg1" position: 0 - typeHint: "Symfony\Component\VarDumper\Tests\Caster\NotExistingClass" + typeHint: "Symfony\Component\VarDumper\Tests\Fixtures\NotLoadableClass" default: null } EOTXT @@ -121,6 +122,6 @@ class: "Symfony\Component\VarDumper\Tests\Caster\ReflectionCasterTest" } } -function reflectionParameterFixture(NotExistingClass $arg1 = null, $arg2) +function reflectionParameterFixture(NotLoadableClass $arg1 = null, $arg2) { } diff --git a/src/Symfony/Component/VarDumper/Tests/Fixtures/NotLoadableClass.php b/src/Symfony/Component/VarDumper/Tests/Fixtures/NotLoadableClass.php new file mode 100644 index 0000000000000..d8e0cb359f4fd --- /dev/null +++ b/src/Symfony/Component/VarDumper/Tests/Fixtures/NotLoadableClass.php @@ -0,0 +1,7 @@ +