diff --git a/.composer/composer.json b/.composer/composer.json deleted file mode 100644 index d69e9f7626d55..0000000000000 --- a/.composer/composer.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "require": { - "hirak/prestissimo": "^0.1.18" - } -} diff --git a/.composer/composer.lock b/.composer/composer.lock deleted file mode 100644 index 7027e21d9ac4b..0000000000000 --- a/.composer/composer.lock +++ /dev/null @@ -1,70 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", - "This file is @generated automatically" - ], - "hash": "e02685ff7d2409d34fa87e306fc86ec5", - "content-hash": "2854c05c76a78113c693dbbdd3f9c518", - "packages": [ - { - "name": "hirak/prestissimo", - "version": "0.1.18", - "source": { - "type": "git", - "url": "https://github.com/hirak/prestissimo.git", - "reference": "84e9fb79ec18a4428c5a0c032eacac640d89be5d" - }, - "dist": { - "type": "zip", - "url": "https://codeload.github.com/hirak/prestissimo/legacy.zip/84e9fb79ec18a4428c5a0c032eacac640d89be5d", - "reference": "84e9fb79ec18a4428c5a0c032eacac640d89be5d", - "shasum": "" - }, - "require": { - "composer-plugin-api": "~1.0.0-alpha10", - "ext-curl": "*", - "php": ">=5.3.2" - }, - "require-dev": { - "composer/composer": "1.0.0-alpha10", - "phpunit/phpunit": "^4.8", - "squizlabs/php_codesniffer": "^2.5" - }, - "type": "composer-plugin", - "extra": { - "class": "Hirak\\Prestissimo\\Plugin" - }, - "autoload": { - "psr-4": { - "Hirak\\Prestissimo\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Hiraku NAKANO", - "email": "hiraku@tojiru.net" - } - ], - "description": "composer parallel install plugin", - "keywords": [ - "install", - "parallel", - "speedup" - ], - "time": "2016-03-17 13:53:53" - } - ], - "packages-dev": [], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": [], - "platform-dev": [] -} diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7ed7b20e228ae..71c775edb8ae7 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,6 @@ | Q | A | ------------- | --- -| Branch? | "master" for new features / 2.3, 2.7, 2.8 or 3.0 for fixes +| Branch? | "master" for new features / 2.7, 2.8, 3.0 or 3.1 for fixes | Bug fix? | yes/no | New feature? | yes/no | BC breaks? | yes/no diff --git a/.travis.php b/.travis.php index fdf223bb7a719..c6e33e25d840f 100644 --- a/.travis.php +++ b/.travis.php @@ -1,20 +1,19 @@ $_SERVER['argc']) { - echo "Usage: commit-range branch dir1 dir2 ... dirN\n"; + echo "Usage: branch dir1 dir2 ... dirN\n"; exit(1); } $dirs = $_SERVER['argv']; array_shift($dirs); -$range = array_shift($dirs); $branch = array_shift($dirs); $packages = array(); $flags = PHP_VERSION_ID >= 50400 ? JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE : 0; foreach ($dirs as $dir) { - if (!`git diff --name-only $range -- $dir`) { + if (!`git diff --name-only $branch...HEAD -- $dir`) { continue; } echo "$dir\n"; diff --git a/.travis.yml b/.travis.yml index 5e62e94e79ba7..0402b856474ec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,11 @@ env: matrix: include: + # Use the newer stack for HHVM as HHVM does not support Precise anymore since a long time and so Precise has an outdated version - php: hhvm + sudo: required + dist: trusty + group: edge - php: 5.5 - php: 5.6 env: deps=high @@ -33,12 +37,13 @@ cache: services: mongodb before_install: + - stty cols 120 - PHP=$TRAVIS_PHP_VERSION # Matrix lines for intermediate PHP versions are skipped for pull requests - - if [[ ! $deps && ! $PHP = ${MIN_PHP%.*} && $PHP != hhvm && $TRAVIS_PULL_REQUEST != false ]]; then deps=skip; skip=1; 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 && $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 [[ ! $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 @@ -47,18 +52,17 @@ before_install: - 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 && ! $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 cp .composer/* ~/.composer/; 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 [[ ! $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 [[ ! $skip && $deps ]]; then php .travis.php $TRAVIS_COMMIT_RANGE $TRAVIS_BRANCH $COMPONENTS; fi + - if [[ ! $skip && $deps ]]; then php .travis.php $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 @@ -67,12 +71,13 @@ install: - 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 + - if [[ ! $PHP = hhvm* ]]; then php -i; else hhvm --php -r 'print_r($_SERVER);print_r(ini_get_all());'; fi script: - 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 = hhvm* ]]; then echo "$COMPONENTS" | parallel --gnu '$PHPUNIT --exclude-group tty,benchmark,intl-data {}'; fi + - if [[ ! $deps && ! $PHP = hhvm* ]]; then echo -e "\\nRunning tests requiring tty"; $PHPUNIT --group tty; fi + - if [[ ! $deps && $PHP = hhvm* ]]; then $PHPUNIT --exclude-group benchmark,intl-data; 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-3.0.md b/CHANGELOG-3.0.md index 4d98c24a93e44..00be856735c24 100644 --- a/CHANGELOG-3.0.md +++ b/CHANGELOG-3.0.md @@ -7,6 +7,37 @@ in 3.0 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v3.0.0...v3.0.1 +* 3.0.7 (2016-06-06) + + * bug #18908 [DependencyInjection] force enabling the external XML entity loaders (xabbuh) + * bug #18893 [DependencyInjection] Skip deep reference check for 'service_container' (RobertMe) + * bug #18812 Catch \Throwable (fprochazka) + * bug #18821 [Form] Removed UTC specification with timestamp (francisbesset) + * bug #18861 Fix for #18843 (inso) + * bug #18889 [Console] SymfonyStyle: Fix alignment/prefixing of multi-line comments (chalasr) + * bug #18907 [Routing] Fix the annotation loader taking a class constant as a beginning of a class name (jakzal, nicolas-grekas) + * bug #18879 [Console] SymfonyStyle: Align multi-line/very-long-line blocks (chalasr) + * bug #18864 [Console][DX] Fixed ambiguous error message when using a duplicate option shortcut (peterrehm) + * bug #18883 Fix js comment in profiler (linnaea) + * bug #18844 [Yaml] fix exception contexts (xabbuh) + * bug #18840 [Yaml] properly handle unindented collections (xabbuh) + * bug #18765 Catch \Throwable (fprochazka) + * bug #18813 Catch \Throwable (fprochazka) + * bug #18839 People - person singularization (Keeo) + * bug #18828 [Yaml] chomp newlines only at the end of YAML documents (xabbuh) + * bug #18814 Fixed server status command when port has been omitted (peterrehm) + * bug #18759 [Validator] Support for DateTimeImmutable (krzysiekpiasecki) + * bug #18799 Use levenshtein level for better Bundle matching (j0k3r) + * bug #18413 [WebProfilerBundle] Fix CORS ajax security issues (romainneutron) + * bug #18774 [console][table] adjust width of colspanned cell. (aitboudad) + * bug #18507 [BUG] Delete class 'control-group' in bootstrap 3 (Philippe Degeeter) + * bug #18747 [Form] Modified iterator_to_array's 2nd parameter to false in ViolationMapper (issei-m) + * bug #18635 [Console] Prevent fatal error when calling Command::getHelper without helperSet (chalasr) + * bug #18686 [console][table] adjust width of colspanned cell. (aitboudad) + * bug #18761 [Form] Modified iterator_to_array's 2nd parameter to false in ViolationMapper (issei-m) + * bug #18745 [MonologBridge] Uninstallable together with symfony/http-kernel in 3.0.6 (ymc-dabe) + * bug #18737 [Debug] Fix fatal error handlers on PHP 7 (nicolas-grekas) + * 3.0.6 (2016-05-10) * security #18736 Fixed issue with blank password with Ldap (csarrazi) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 456aeba37aa74..94711183ff113 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -23,8 +23,8 @@ Symfony is the result of the work of many people who made the code better - Pascal Borreli (pborreli) - Joseph Bielawski (stloyd) - Wouter De Jong (wouterj) - - Karma Dordrak (drak) - Romain Neutron (romain) + - Karma Dordrak (drak) - Lukas Kahwe Smith (lsmith) - Martin Hasoň (hason) - Jeremy Mikola (jmikola) @@ -40,9 +40,9 @@ Symfony is the result of the work of many people who made the code better - Alexandre Salomé (alexandresalome) - William Durand (couac) - ornicar + - Jules Pietri (heah) - stealth35 ‏ (stealth35) - Alexander Mols (asm89) - - Jules Pietri (heah) - Francis Besset (francisbesset) - Bulat Shakirzyanov (avalanche123) - Saša Stamenković (umpirsky) @@ -53,11 +53,11 @@ Symfony is the result of the work of many people who made the code better - Bilal Amarni (bamarni) - Florin Patan (florinpatan) - Kevin Bond (kbond) + - Peter Rehm (rpet) - Gábor Egyed (1ed) - Michel Weimerskirch (mweimerskirch) - Eric Clemmons (ericclemmons) - Andrej Hudec (pulzarraider) - - Peter Rehm (rpet) - Christian Raue - Matthias Pigulla (mpdude) - Deni @@ -67,6 +67,7 @@ Symfony is the result of the work of many people who made the code better - Iltar van der Berg (kjarli) - Ener-Getick (energetick) - Douglas Greenshields (shieldo) + - Charles Sarrazin (csarrazi) - Lee McDermott - Brandon Turner - Luis Cordova (cordoval) @@ -74,7 +75,6 @@ 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) @@ -89,6 +89,7 @@ Symfony is the result of the work of many people who made the code better - Alexander M. Turek (derrabus) - Dariusz Ruminski - marc.weistroff + - Issei Murasawa (issei_m) - lenar - Włodzimierz Gajda (gajdaw) - Alexander Schwenn (xelaris) @@ -97,13 +98,12 @@ Symfony is the result of the work of many people who made the code better - Colin Frei - Adrien Brault (adrienbrault) - Joshua Thijssen - - Issei Murasawa (issei_m) + - Baptiste Clavié (talus) - Peter Kokot (maastermedia) - excelwebzone - Jacob Dreesen (jdreesen) - Jérémy DERUSSÉ (jderusse) - Vladimir Reznichenko (kalessil) - - Baptiste Clavié (talus) - Fabien Pennequin (fabienpennequin) - Gordon Franke (gimler) - David Buchmann (dbu) @@ -121,6 +121,7 @@ Symfony is the result of the work of many people who made the code better - Evgeniy (ewgraf) - Guilherme Blanco (guilhermeblanco) - Pablo Godel (pgodel) + - Titouan Galopin (tgalopin) - Jérémie Augustin (jaugustin) - Sebastiaan Stok (sstok) - Tugdual Saunier (tucksaun) @@ -132,6 +133,7 @@ Symfony is the result of the work of many people who made the code better - Richard Shank (iampersistent) - Thomas Rabaix (rande) - Vincent AUBERT (vincent) + - Rouven Weßling (realityking) - Mikael Pajunen - Clemens Tolboom - Helmer Aaviksoo @@ -141,9 +143,7 @@ Symfony is the result of the work of many people who made the code better - 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) @@ -211,6 +211,7 @@ Symfony is the result of the work of many people who made the code better - Kristen Gilden (kgilden) - Dawid Nowak - Pierre-Yves LEBECQ (pylebecq) + - Daniel Espendiller - Jakub Kucharovic (jkucharovic) - Eugene Leonovich (rybakit) - Filippo Tessarotto @@ -292,6 +293,7 @@ Symfony is the result of the work of many people who made the code better - Inal DJAFAR (inalgnu) - Christian Gärtner (dagardner) - Tomasz Kowalczyk (thunderer) + - Christian Schmidt - François-Xavier de Guillebon (de-gui_f) - Damien Alexandre (damienalexandre) - Felix Labrecque @@ -308,6 +310,7 @@ Symfony is the result of the work of many people who made the code better - Philipp Kräutli (pkraeutli) - Kirill chEbba Chebunin (chebba) - Greg Thornton (xdissent) + - Robin Chalas (chalas_r) - Costin Bereveanu (schniper) - Loïc Chardonnet (gnusat) - Marek Kalnik (marekkalnik) @@ -318,12 +321,14 @@ Symfony is the result of the work of many people who made the code better - Pavel Volokitin (pvolok) - Endre Fejes - Tobias Naumann (tna) + - Daniel Beyer - Shein Alexey - Baptiste Lafontaine - Joe Lencioni - Daniel Tschinder - Kai - Lee Rowlands + - Krzysztof Piasecki (krzysztek) - Maximilian Reichel (phramz) - Loick Piera (pyrech) - Karoly Negyesi (chx) @@ -363,7 +368,6 @@ Symfony is the result of the work of many people who made the code better - EdgarPE - Florian Pfitzer (marmelatze) - Asier Illarramendi (doup) - - Christian Schmidt - Chris Sedlmayr (catchamonkey) - Seb Koelen - Christoph Mewes (xrstf) @@ -381,6 +385,7 @@ Symfony is the result of the work of many people who made the code better - Mathieu Lemoine - franek (franek) - Christian Wahler + - Mathieu Lemoine - Gintautas Miselis - Zander Baldwin - Adam Harvey @@ -418,7 +423,6 @@ Symfony is the result of the work of many people who made the code better - Anthony Ferrara - Ioan Negulescu - Jakub Škvára (jskvara) - - Daniel Beyer - Andrew Udvare (audvare) - alexpods - Tristan Darricau (nicofuma) @@ -436,6 +440,7 @@ Symfony is the result of the work of many people who made the code better - Benjamin Leveque (benji07) - Nate (frickenate) - jhonnyL + - Jhonny Lidfors (jhonne) - sasezaki - Dawid Pakuła (zulusx) - Florian Rey (nervo) @@ -446,6 +451,7 @@ Symfony is the result of the work of many people who made the code better - Denis Gorbachev (starfall) - Peter van Dommelen - Tim van Densen + - Martin Morávek (keeo) - Steven Surowiec - Kevin Saliou (kbsali) - NothingWeAre @@ -460,7 +466,6 @@ Symfony is the result of the work of many people who made the code better - Ziumin - Jeremy Benoist - Lenar Lõhmus - - Krzysztof Piasecki (krzysztek) - Benjamin Laugueux (yzalis) - Zach Badgett (zachbadgett) - Aurélien Fredouelle @@ -499,6 +504,7 @@ Symfony is the result of the work of many people who made the code better - Javier López (loalf) - Reinier Kip - Dustin Dobervich (dustin10) + - dantleech - Anne-Sophie Bachelard (annesophie) - Sebastian Marek (proofek) - Erkhembayar Gantulga (erheme318) @@ -561,6 +567,7 @@ Symfony is the result of the work of many people who made the code better - Peter Ward - Dominik Ritter (dritter) - Sebastian Grodzicki (sgrodzicki) + - SpacePossum - Martin Hujer (martinhujer) - Pascal Helfenstein - Baldur Rensch (brensch) @@ -611,7 +618,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) + - Filip Procházka (fprochazka) - Christopher Hall (mythmakr) - Paul Kamer (pkamer) - Rafał Wrzeszcz (rafalwrzeszcz) @@ -628,6 +635,7 @@ Symfony is the result of the work of many people who made the code better - Alaattin Kahramanlar (alaattin) - Maksim Kotlyar (makasim) - Neil Ferreira + - Nathanael Noblet (gnat) - Dmitry Parnas (parnas) - Théo FIDRY (theofidry) - Paul LE CORRE @@ -744,7 +752,6 @@ Symfony is the result of the work of many people who made the code better - Guillaume Royer - Artem (digi) - boite - - dantleech - Vadim Tyukov (vatson) - Sortex - chispita @@ -850,6 +857,7 @@ Symfony is the result of the work of many people who made the code better - Christian Sciberras - Anton Bakai - Clement Herreman (clemherreman) + - Dan Ionut Dumitriu (danionut90) - Nyro (nyro) - Marco - Marc Torres @@ -869,7 +877,6 @@ Symfony is the result of the work of many people who made the code better - ConneXNL - Aharon Perkel - Abdul.Mohsen B. A. A - - SpacePossum - Benoît Burnichon - Remi Collet - pthompson @@ -904,7 +911,6 @@ Symfony is the result of the work of many people who made the code better - Albin Kerouaton - Sébastien HOUZÉ - Jingyu Wang - - Daniel Espendiller - steveYeah - Samy Dindane (dinduks) - Keri Henare (kerihenare) @@ -945,6 +951,7 @@ Symfony is the result of the work of many people who made the code better - Tadcka - Beth Binkovitz - Romain Geissler + - Oliver Hoff - Tomaz Ahlin - Benjamin Cremer (bcremer) - Marcus Stöhr (dafish) @@ -1058,6 +1065,7 @@ Symfony is the result of the work of many people who made the code better - stloyd - Chris Tickner - Andrew Coulton + - Jeremy Benoist - Michal Gebauer - Gleb Sidora - David Stone @@ -1107,7 +1115,6 @@ Symfony is the result of the work of many people who made the code better - Simon Sargeant - efeen - Michał Dąbrowski (defrag) - - Nathanael Noblet (gnat) - Simone Fumagalli (hpatoio) - Brian Graham (incognito) - Kevin Vergauwen (innocenzo) @@ -1213,8 +1220,10 @@ Symfony is the result of the work of many people who made the code better - Alan Chen - Maerlyn - Even André Fiskvik + - Erik van Wingerden - Dane Powell - Gerrit Drost + - Linnaea Von Lavia - Lenar Lõhmus - Cristian Gonzalez - AlberT @@ -1270,6 +1279,7 @@ Symfony is the result of the work of many people who made the code better - Joseph Deray - Damian Sromek - Ben + - dasmfm - Arnaud Buathier (arnapou) - chesteroni (chesteroni) - Mauricio Lopez (diaspar) @@ -1302,6 +1312,7 @@ Symfony is the result of the work of many people who made the code better - znerol - Christian Eikermann - Antonio Angelino + - Shawn Iwinski - Vladimir Sazhin - lol768 - jamogon @@ -1356,7 +1367,6 @@ 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 @@ -1441,6 +1451,7 @@ Symfony is the result of the work of many people who made the code better - ollie harridge (ollietb) - Paul Andrieux (paulandrieux) - Paweł Szczepanek (pauluz) + - Philippe Degeeter (pdegeeter) - Christian López Espínola (penyaskito) - Petr Jaroš (petajaros) - Philipp Hoffmann (philipphoffmann) diff --git a/UPGRADE-3.0.md b/UPGRADE-3.0.md index 8925b911890e0..f9933d8b63544 100644 --- a/UPGRADE-3.0.md +++ b/UPGRADE-3.0.md @@ -398,8 +398,11 @@ UPGRADE FROM 2.x to 3.0 ``` - * The `ChoiceToBooleanArrayTransformer`, `ChoicesToBooleanArrayTransformer`, - `FixRadioInputListener`, and `FixCheckboxInputListener` classes were removed. + * The `max_length` option was removed. Use the `attr` option instead by setting it to + an `array` with a `maxlength` key. + + * The `ChoiceToBooleanArrayTransformer`, `ChoicesToBooleanArrayTransformer`, + `FixRadioInputListener`, and `FixCheckboxInputListener` classes were removed. * The `choice_list` option of `ChoiceType` was removed. @@ -636,6 +639,11 @@ UPGRADE FROM 2.x to 3.0 be removed in Symfony 3.0. Use the `debug:config`, `debug:container`, `debug:router`, `debug:translation` and `lint:yaml` commands instead. + * The base `Controller`class is now abstract. + + * The visibility of all methods of the base `Controller` class has been changed from + `public` to `protected`. + * The `getRequest` method of the base `Controller` class has been deprecated since Symfony 2.4 and must be therefore removed in 3.0. The only reliable way to get the `Request` object is to inject it in the action method. @@ -1075,7 +1083,7 @@ UPGRADE FROM 2.x to 3.0 } ``` - * The `AbstractVoter::isGranted()` method have been replaced by `AbstractVoter::voteOnAttribute()`. + * The `AbstractVoter::isGranted()` method has been replaced by `Voter::voteOnAttribute()`. Before: @@ -1094,7 +1102,7 @@ UPGRADE FROM 2.x to 3.0 After: ```php - class MyVoter extends AbstractVoter + class MyVoter extends Voter { protected function voteOnAttribute($attribute, $object, TokenInterface $token) { @@ -1105,8 +1113,8 @@ UPGRADE FROM 2.x to 3.0 } ``` - * The `supportsAttribute()` and `supportsClass()` methods of classes `AuthenticatedVoter`, `ExpressionVoter` - and `RoleVoter` have been removed. + * The `supportsAttribute()` and `supportsClass()` methods of the `AuthenticatedVoter`, `ExpressionVoter`, + and `RoleVoter` classes have been removed. * The `intention` option was renamed to `csrf_token_id` for all the authentication listeners. @@ -1737,8 +1745,7 @@ UPGRADE FROM 2.x to 3.0 ### WebProfiler - * The `profiler:import` and `profiler:export` commands have been deprecated and - will be removed in 3.0. + * The `profiler:import` and `profiler:export` commands have been removed. * All the profiler storages different than `FileProfilerStorage` have been removed. The removed classes are: diff --git a/appveyor.yml b/appveyor.yml index 2a46dd936ded4..6b98bc7a7b49e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -18,7 +18,7 @@ init: install: - IF EXIST c:\php (SET PHP=0) ELSE (mkdir c:\php) - cd c:\php - - IF %PHP%==1 appveyor DownloadFile https://curl.haxx.se/ca/cacert.pem + - IF %PHP%==1 appveyor DownloadFile https://raw.githubusercontent.com/symfony/binary-utils/master/cacert.pem - IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/releases/archives/php-5.5.9-nts-Win32-VC11-x86.zip - IF %PHP%==1 7z x php-5.5.9-nts-Win32-VC11-x86.zip -y >nul - IF %PHP%==1 del /Q *.zip @@ -46,12 +46,11 @@ install: - IF %PHP%==1 echo extension=php_ldap.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 - - IF %PHP%==1 appveyor DownloadFile https://getcomposer.org/download/1.0.2/composer.phar + - IF %PHP%==1 appveyor DownloadFile https://getcomposer.org/download/1.0.3/composer.phar - copy /Y php.ini-max php.ini - cd c:\projects\symfony - mkdir %APPDATA%\Composer - 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/phpunit b/phpunit index 6d66f7b2b252a..3ab3ca1686042 100755 --- a/phpunit +++ b/phpunit @@ -53,6 +53,9 @@ if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit") || md5_file(__ chdir("phpunit-$PHPUNIT_VERSION"); passthru("$COMPOSER remove --no-update phpspec/prophecy"); passthru("$COMPOSER remove --no-update symfony/yaml"); + if (5.1 <= $PHPUNIT_VERSION && $PHPUNIT_VERSION < 5.4) { + passthru("$COMPOSER require --no-update phpunit/phpunit-mock-objects \"~3.1.0\""); + } passthru("$COMPOSER require --dev --no-update symfony/phpunit-bridge \">=3.1@dev\""); passthru("$COMPOSER install --prefer-dist --no-progress --ansi", $exit); if ($exit) { @@ -170,8 +173,11 @@ if (isset($argv[1]) && 'symfony' === $argv[1]) { unlink($file); } - // Fail on any individual component failures but ignore STATUS_STACK_BUFFER_OVERRUN (-1073740791/0xC0000409) and STATUS_ACCESS_VIOLATION (-1073741819/0xC0000005) on Windows when APCu is enabled - if ($procStatus && ('\\' !== DIRECTORY_SEPARATOR || !extension_loaded('apcu') || !ini_get('apc.enable_cli') || (-1073740791 !== $procStatus && -1073741819 !== $procStatus))) { + // Fail on any individual component failures but ignore some error codes on Windows when APCu is enabled: + // STATUS_STACK_BUFFER_OVERRUN (-1073740791/0xC0000409) + // STATUS_ACCESS_VIOLATION (-1073741819/0xC0000005) + // STATUS_HEAP_CORRUPTION (-1073740940/0xC0000374) + if ($procStatus && ('\\' !== DIRECTORY_SEPARATOR || !extension_loaded('apcu') || !ini_get('apc.enable_cli') || !in_array($procStatus, array(-1073740791, -1073741819, -1073740940)))) { $exit = $procStatus; echo "\033[41mKO\033[0m $component\n\n"; } else { diff --git a/src/Symfony/Bridge/Monolog/composer.json b/src/Symfony/Bridge/Monolog/composer.json index 6d94761630a26..9e943ad2e640f 100644 --- a/src/Symfony/Bridge/Monolog/composer.json +++ b/src/Symfony/Bridge/Monolog/composer.json @@ -24,9 +24,6 @@ "symfony/console": "~2.8|~3.0", "symfony/event-dispatcher": "~2.8|~3.0" }, - "conflict": { - "symfony/http-kernel": ">=3.0" - }, "suggest": { "symfony/http-kernel": "For using the debugging handlers together with the response life cycle of the HTTP kernel.", "symfony/console": "For the possibility to show log messages in console commands depending on verbosity settings. You need version ~2.3 of the console for it.", diff --git a/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php b/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php index 1712bd5afee8e..f9333bf683d1a 100644 --- a/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php +++ b/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php @@ -67,9 +67,9 @@ public function leave() * @param string $key * @param mixed $value * - * @throws \LogicException - * * @return Scope Current scope + * + * @throws \LogicException */ public function set($key, $value) { diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig index 0fb3f769b8391..ef2035ab43f04 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig @@ -94,14 +94,12 @@ {% block choice_widget_expanded -%} {% if '-inline' in label_attr.class|default('') -%} -
- {%- for child in form %} - {{- form_widget(child, { - parent_label_class: label_attr.class|default(''), - translation_domain: choice_translation_domain, - }) -}} - {% endfor -%} -
+ {%- for child in form %} + {{- form_widget(child, { + parent_label_class: label_attr.class|default(''), + translation_domain: choice_translation_domain, + }) -}} + {% endfor -%} {%- else -%}
{%- for child in form %} diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ServerStatusCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ServerStatusCommand.php index b6ec558701a14..d7cb9e7d08197 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ServerStatusCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ServerStatusCommand.php @@ -13,6 +13,7 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; @@ -32,6 +33,7 @@ protected function configure() $this ->setDefinition(array( new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1:8000'), + new InputOption('port', 'p', InputOption::VALUE_REQUIRED, 'Address port number', '8000'), )) ->setName('server:status') ->setDescription('Outputs the status of the built-in web server for the given address') @@ -46,6 +48,10 @@ protected function execute(InputInterface $input, OutputInterface $output) $io = new SymfonyStyle($input, $output); $address = $input->getArgument('address'); + if (false === strpos($address, ':')) { + $address = $address.':'.$input->getOption('port'); + } + // remove an orphaned lock file if (file_exists($this->getLockFile($address)) && !$this->isServerRunning($address)) { unlink($this->getLockFile($address)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php index fe2ecd95ad18b..25169e9bb68f4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php @@ -322,7 +322,7 @@ protected function describeContainerDefinition(Definition $definition, array $op */ protected function describeContainerAlias(Alias $alias, array $options = array()) { - $options['output']->comment(sprintf("This service is an alias for the service %s\n", (string) $alias)); + $options['output']->comment(sprintf('This service is an alias for the service %s', (string) $alias)); } /** diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php index 873c736841ba8..374d0eba10ff6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php @@ -142,6 +142,7 @@ private function findAlternative($nonExistentBundleName) $lev = levenshtein($nonExistentBundleName, $bundleName); if ($lev <= strlen($nonExistentBundleName) / 3 && ($alternative === null || $lev < $shortest)) { $alternative = $bundleName; + $shortest = $lev; } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php b/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php index ce06c6169bf53..68ec13a46499e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php +++ b/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php @@ -78,7 +78,7 @@ public function load($resource, $type = null) if ($controller = $route->getDefault('_controller')) { try { $controller = $this->parser->parse($controller); - } catch (\Exception $e) { + } catch (\InvalidArgumentException $e) { // unable to optimize unknown notation } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerNameParserTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerNameParserTest.php index 4718c1eb0b238..0fe47b0908d83 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerNameParserTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerNameParserTest.php @@ -59,8 +59,8 @@ public function testBuild() { $parser = $this->createParser(); - $this->assertEquals('FooBundle:Default:index', $parser->build('TestBundle\FooBundle\Controller\DefaultController::indexAction'), '->parse() converts a class::method string to a short a:b:c notation string'); - $this->assertEquals('FooBundle:Sub\Default:index', $parser->build('TestBundle\FooBundle\Controller\Sub\DefaultController::indexAction'), '->parse() converts a class::method string to a short a:b:c notation string'); + $this->assertEquals('FoooooBundle:Default:index', $parser->build('TestBundle\FooBundle\Controller\DefaultController::indexAction'), '->parse() converts a class::method string to a short a:b:c notation string'); + $this->assertEquals('FoooooBundle:Sub\Default:index', $parser->build('TestBundle\FooBundle\Controller\Sub\DefaultController::indexAction'), '->parse() converts a class::method string to a short a:b:c notation string'); try { $parser->build('TestBundle\FooBundle\Controller\DefaultController::index'); @@ -132,8 +132,9 @@ public function testInvalidBundleName($bundleName, $suggestedBundleName) public function getInvalidBundleNameTests() { return array( - array('FoodBundle:Default:index', 'FooBundle:Default:index'), - array('CrazyBundle:Default:index', false), + 'Alternative will be found using levenshtein' => array('FoodBundle:Default:index', 'FooBundle:Default:index'), + 'Alternative will be found using partial match' => array('FabpotFooBund:Default:index', 'FabpotFooBundle:Default:index'), + 'Bundle does not exist at all' => array('CrazyBundle:Default:index', false), ); } @@ -162,6 +163,7 @@ private function createParser() $bundles = array( 'SensioFooBundle' => $this->getBundle('TestBundle\Fabpot\FooBundle', 'FabpotFooBundle'), 'SensioCmsFooBundle' => $this->getBundle('TestBundle\Sensio\Cms\FooBundle', 'SensioCmsFooBundle'), + 'FoooooBundle' => $this->getBundle('TestBundle\FooBundle', 'FoooooBundle'), 'FooBundle' => $this->getBundle('TestBundle\FooBundle', 'FooBundle'), 'FabpotFooBundle' => $this->getBundle('TestBundle\Fabpot\FooBundle', 'FabpotFooBundle'), ); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php index 6974cd1ffd692..b511f90d17adb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php @@ -154,7 +154,7 @@ public function getUndefinedControllers() { return array( array('foo', '\LogicException', '/Unable to parse the controller name "foo"\./'), - array('foo::bar', '\InvalidArgumentException', '/Class "foo" does not exist\./'), + array('oof::bar', '\InvalidArgumentException', '/Class "oof" does not exist\./'), array('stdClass', '\LogicException', '/Unable to parse the controller name "stdClass"\./'), array( 'Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest::bar', diff --git a/src/Symfony/Bundle/FrameworkBundle/Translation/PhpExtractor.php b/src/Symfony/Bundle/FrameworkBundle/Translation/PhpExtractor.php index ca9e7f994c090..cf7f3c5769bf2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Translation/PhpExtractor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Translation/PhpExtractor.php @@ -182,9 +182,9 @@ protected function parseTokens($tokens, MessageCatalogue $catalog) /** * @param string $file * - * @throws \InvalidArgumentException - * * @return bool + * + * @throws \InvalidArgumentException */ protected function canBeExtracted($file) { diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig index 408e5c6bdcbf3..03617dfa957b3 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig @@ -82,6 +82,20 @@ requestStack = [], + extractHeaders = function(xhr, stackElement) { + /* Here we avoid to call xhr.getResponseHeader in order to */ + /* prevent polluting the console with CORS security errors */ + var allHeaders = xhr.getAllResponseHeaders(); + var ret; + + if (ret = allHeaders.match(/^x-debug-token:\s+(.*)$/im)) { + stackElement.profile = ret[1]; + } + if (ret = allHeaders.match(/^x-debug-token-link:\s+(.*)$/im)) { + stackElement.profilerUrl = ret[1]; + } + }, + renderAjaxRequests = function() { var requestCounter = document.querySelectorAll('.sf-toolbar-ajax-requests'); if (!requestCounter.length) { @@ -241,8 +255,8 @@ stackElement.duration = new Date() - stackElement.start; stackElement.loading = false; stackElement.error = self.status < 200 || self.status >= 400; - stackElement.profile = self.getResponseHeader("X-Debug-Token"); - stackElement.profilerUrl = self.getResponseHeader("X-Debug-Token-Link"); + + extractHeaders(self, stackElement); Sfjs.renderAjaxRequests(); } diff --git a/src/Symfony/Component/Asset/Tests/VersionStrategy/StaticVersionStrategyTest.php b/src/Symfony/Component/Asset/Tests/VersionStrategy/StaticVersionStrategyTest.php new file mode 100644 index 0000000000000..77958ce2ca0cc --- /dev/null +++ b/src/Symfony/Component/Asset/Tests/VersionStrategy/StaticVersionStrategyTest.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Asset\Tests\VersionStrategy; + +use Symfony\Component\Asset\VersionStrategy\StaticVersionStrategy; + +class StaticVersionStrategyTest extends \PHPUnit_Framework_TestCase +{ + public function testGetVersion() + { + $version = 'v1'; + $path = 'test-path'; + $staticVersionStrategy = new StaticVersionStrategy($version); + $this->assertEquals($version, $staticVersionStrategy->getVersion($path)); + } + + /** + * @dataProvider getConfigs + */ + public function testApplyVersion($path, $version, $format) + { + $staticVersionStrategy = new StaticVersionStrategy($version, $format); + $formatted = sprintf($format ?: '%s?%s', $path, $version); + $this->assertEquals($formatted, $staticVersionStrategy->applyVersion($path)); + } + + public function getConfigs() + { + return array( + array('test-path', 'v1', null), + array('test-path', 'v2', '%s?test%s'), + ); + } +} diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php index c8429845a2634..d212a3254752a 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php @@ -300,14 +300,14 @@ public function mergeApplicationDefinition($mergeArgs = true) return; } + $this->definition->addOptions($this->application->getDefinition()->getOptions()); + if ($mergeArgs) { $currentArguments = $this->definition->getArguments(); $this->definition->setArguments($this->application->getDefinition()->getArguments()); $this->definition->addArguments($currentArguments); } - $this->definition->addOptions($this->application->getDefinition()->getOptions()); - $this->applicationDefinitionMerged = true; if ($mergeArgs) { $this->applicationDefinitionMergedWithArgs = true; @@ -601,10 +601,15 @@ public function getUsages() * * @return mixed The helper value * + * @throws LogicException if no HelperSet is defined * @throws InvalidArgumentException if the helper is not defined */ public function getHelper($name) { + if (null === $this->helperSet) { + throw new LogicException(sprintf('Cannot retrieve helper "%s" because there is no HelperSet defined. Did you forget to add your command to the application or to set the application on the command using the setApplication() method? You can also set the HelperSet directly using the setHelperSet() method.', $name)); + } + return $this->helperSet->get($name); } diff --git a/src/Symfony/Component/Console/Helper/ProgressIndicator.php b/src/Symfony/Component/Console/Helper/ProgressIndicator.php index ccf9771bcf9e4..f90a85c2918ca 100644 --- a/src/Symfony/Component/Console/Helper/ProgressIndicator.php +++ b/src/Symfony/Component/Console/Helper/ProgressIndicator.php @@ -76,42 +76,6 @@ public function setMessage($message) $this->display(); } - /** - * Gets the current indicator message. - * - * @return string|null - * - * @internal for PHP 5.3 compatibility - */ - public function getMessage() - { - return $this->message; - } - - /** - * Gets the progress bar start time. - * - * @return int The progress bar start time - * - * @internal for PHP 5.3 compatibility - */ - public function getStartTime() - { - return $this->startTime; - } - - /** - * Gets the current animated indicator character. - * - * @return string - * - * @internal for PHP 5.3 compatibility - */ - public function getCurrentValue() - { - return $this->indicatorValues[$this->indicatorCurrent % count($this->indicatorValues)]; - } - /** * Starts the indicator output. * @@ -294,13 +258,13 @@ private static function initPlaceholderFormatters() { return array( 'indicator' => function (ProgressIndicator $indicator) { - return $indicator->getCurrentValue(); + return $indicator->indicatorValues[$indicator->indicatorCurrent % count($indicator->indicatorValues)]; }, 'message' => function (ProgressIndicator $indicator) { - return $indicator->getMessage(); + return $indicator->message; }, 'elapsed' => function (ProgressIndicator $indicator) { - return Helper::formatTime(time() - $indicator->getStartTime()); + return Helper::formatTime(time() - $indicator->startTime); }, 'memory' => function () { return Helper::formatMemory(memory_get_usage(true)); diff --git a/src/Symfony/Component/Console/Helper/Table.php b/src/Symfony/Component/Console/Helper/Table.php index dc038e4707c16..13e4c3cf6b519 100644 --- a/src/Symfony/Component/Console/Helper/Table.php +++ b/src/Symfony/Component/Console/Helper/Table.php @@ -307,7 +307,7 @@ private function renderRowSeparator() */ private function renderColumnSeparator() { - $this->output->write(sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar())); + return sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar()); } /** @@ -324,12 +324,12 @@ private function renderRow(array $row, $cellFormat) return; } - $this->renderColumnSeparator(); + $rowContent = $this->renderColumnSeparator(); foreach ($this->getRowColumns($row) as $column) { - $this->renderCell($row, $column, $cellFormat); - $this->renderColumnSeparator(); + $rowContent .= $this->renderCell($row, $column, $cellFormat); + $rowContent .= $this->renderColumnSeparator(); } - $this->output->writeln(''); + $this->output->writeln($rowContent); } /** @@ -358,12 +358,13 @@ private function renderCell(array $row, $column, $cellFormat) $style = $this->getColumnStyle($column); if ($cell instanceof TableSeparator) { - $this->output->write(sprintf($style->getBorderFormat(), str_repeat($style->getHorizontalBorderChar(), $width))); - } else { - $width += Helper::strlen($cell) - Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell); - $content = sprintf($style->getCellRowContentFormat(), $cell); - $this->output->write(sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $style->getPadType()))); + return sprintf($style->getBorderFormat(), str_repeat($style->getHorizontalBorderChar(), $width)); } + + $width += Helper::strlen($cell) - Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell); + $content = sprintf($style->getCellRowContentFormat(), $cell); + + return sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $style->getPadType())); } /** @@ -569,6 +570,18 @@ private function calculateColumnsWidth($rows) continue; } + foreach ($row as $i => $cell) { + if ($cell instanceof TableCell) { + $textLength = strlen($cell); + if ($textLength > 0) { + $contentColumns = str_split($cell, ceil($textLength / $cell->getColspan())); + foreach ($contentColumns as $position => $content) { + $row[$i + $position] = $content; + } + } + } + } + $lengths[] = $this->getCellWidth($row, $column); } @@ -599,10 +612,6 @@ private function getCellWidth(array $row, $column) if (isset($row[$column])) { $cell = $row[$column]; $cellWidth = Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell); - if ($cell instanceof TableCell && $cell->getColspan() > 1) { - // we assume that cell value will be across more than one column. - $cellWidth = $cellWidth / $cell->getColspan(); - } return $cellWidth; } diff --git a/src/Symfony/Component/Console/Output/StreamOutput.php b/src/Symfony/Component/Console/Output/StreamOutput.php index 098966286eeb5..9e6b74810d282 100644 --- a/src/Symfony/Component/Console/Output/StreamOutput.php +++ b/src/Symfony/Component/Console/Output/StreamOutput.php @@ -72,7 +72,7 @@ public function getStream() */ protected function doWrite($message, $newline) { - if (false === @fwrite($this->stream, $message.($newline ? PHP_EOL : ''))) { + if (false === @fwrite($this->stream, $message) || ($newline && (false === @fwrite($this->stream, PHP_EOL)))) { // should never happen throw new RuntimeException('Unable to write output.'); } diff --git a/src/Symfony/Component/Console/Style/SymfonyStyle.php b/src/Symfony/Component/Console/Style/SymfonyStyle.php index 6086918ac7840..53a7951e016d7 100644 --- a/src/Symfony/Component/Console/Style/SymfonyStyle.php +++ b/src/Symfony/Component/Console/Style/SymfonyStyle.php @@ -68,23 +68,36 @@ public function block($messages, $type = null, $style = null, $prefix = ' ', $pa { $this->autoPrependBlock(); $messages = is_array($messages) ? array_values($messages) : array($messages); + $indentLength = 0; $lines = array(); - // add type if (null !== $type) { - $messages[0] = sprintf('[%s] %s', $type, $messages[0]); + $typePrefix = sprintf('[%s] ', $type); + $indentLength = strlen($typePrefix); + $lineIndentation = str_repeat(' ', $indentLength); } // wrap and add newlines for each element foreach ($messages as $key => $message) { $message = OutputFormatter::escape($message); - $lines = array_merge($lines, explode(PHP_EOL, wordwrap($message, $this->lineLength - Helper::strlen($prefix), PHP_EOL, true))); + $lines = array_merge($lines, explode(PHP_EOL, wordwrap($message, $this->lineLength - Helper::strlen($prefix) - $indentLength, PHP_EOL, true))); + + // prefix each line with a number of spaces equivalent to the type length + if (null !== $type) { + foreach ($lines as &$line) { + $line = $lineIndentation === substr($line, 0, $indentLength) ? $line : $lineIndentation.$line; + } + } if (count($messages) > 1 && $key < count($messages) - 1) { $lines[] = ''; } } + if (null !== $type) { + $lines[0] = substr_replace($lines[0], $typePrefix, 0, $indentLength); + } + if ($padding && $this->isDecorated()) { array_unshift($lines, ''); $lines[] = ''; @@ -157,16 +170,18 @@ public function text($message) } /** - * {@inheritdoc} + * Formats a command comment. + * + * @param string|array $message */ public function comment($message) { - $this->autoPrependText(); - $messages = is_array($message) ? array_values($message) : array($message); - foreach ($messages as $message) { - $this->writeln(sprintf(' // %s', $message)); + foreach ($messages as &$message) { + $message = $this->getFormatter()->format($message); } + + $this->block($messages, null, null, ' // '); } /** diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 7eaa28d40d577..c673ec25200da 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -710,6 +710,33 @@ public function testRunReturnsExitCodeOneForExceptionCodeZero() $this->assertSame(1, $exitCode, '->run() returns exit code 1 when exception code is 0'); } + /** + * @expectedException \LogicException + * @expectedExceptionMessage An option with shortcut "e" already exists. + */ + public function testAddingOptionWithDuplicateShortcut() + { + $dispatcher = new EventDispatcher(); + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->setDispatcher($dispatcher); + + $application->getDefinition()->addOption(new InputOption('--env', '-e', InputOption::VALUE_REQUIRED, 'Environment')); + + $application + ->register('foo') + ->setAliases(array('f')) + ->setDefinition(array(new InputOption('survey', 'e', InputOption::VALUE_REQUIRED, 'My option with a shortcut.'))) + ->setCode(function (InputInterface $input, OutputInterface $output) {}) + ; + + $input = new ArrayInput(array('command' => 'foo')); + $output = new NullOutput(); + + $application->run($input, $output); + } + /** * @expectedException \LogicException * @dataProvider getAddingAlreadySetDefinitionElementData diff --git a/src/Symfony/Component/Console/Tests/Command/CommandTest.php b/src/Symfony/Component/Console/Tests/Command/CommandTest.php index c39adab4943fb..53a6009e6c1eb 100644 --- a/src/Symfony/Component/Console/Tests/Command/CommandTest.php +++ b/src/Symfony/Component/Console/Tests/Command/CommandTest.php @@ -173,6 +173,16 @@ public function testGetHelper() $this->assertEquals($formatterHelper->getName(), $command->getHelper('formatter')->getName(), '->getHelper() returns the correct helper'); } + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot retrieve helper "formatter" because there is no HelperSet defined. + */ + public function testGetHelperWithoutHelperSet() + { + $command = new \TestCommand(); + $command->getHelper('formatter'); + } + public function testMergeApplicationDefinition() { $application1 = new Application(); diff --git a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_10.php b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_10.php new file mode 100644 index 0000000000000..4120df9cb6b64 --- /dev/null +++ b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_10.php @@ -0,0 +1,17 @@ +block( + 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum', + 'CUSTOM', + 'fg=white;bg=green', + 'X ', + true + ); +}; diff --git a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_11.php b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_11.php new file mode 100644 index 0000000000000..a2781ddbeafa4 --- /dev/null +++ b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_11.php @@ -0,0 +1,13 @@ +comment( + 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum' + ); +}; diff --git a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_9.php b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_9.php new file mode 100644 index 0000000000000..6420730fd64ad --- /dev/null +++ b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_9.php @@ -0,0 +1,11 @@ +block(array('Custom block', 'Second custom block line'), 'CUSTOM', 'fg=white;bg=green', 'X ', true); +}; diff --git a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_10.txt b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_10.txt new file mode 100644 index 0000000000000..385c6a283c84d --- /dev/null +++ b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_10.txt @@ -0,0 +1,7 @@ + +X [CUSTOM] Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et +X dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea +X commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat +X nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit +X anim id est laborum + diff --git a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_11.txt b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_11.txt new file mode 100644 index 0000000000000..9983af832aa7d --- /dev/null +++ b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_11.txt @@ -0,0 +1,6 @@ + + // Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna + // aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + // Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur + // sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum + diff --git a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt index 738eb9e606010..be4a2db605795 100644 --- a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt +++ b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt @@ -11,5 +11,8 @@ Lorem ipsum dolor sit amet consectetur adipiscing elit Lorem ipsum dolor sit amet - // Lorem ipsum dolor sit amet - // consectetur adipiscing elit + + // Lorem ipsum dolor sit amet + // + // consectetur adipiscing elit + diff --git a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_9.txt b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_9.txt new file mode 100644 index 0000000000000..069c0d5119f7b --- /dev/null +++ b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_9.txt @@ -0,0 +1,5 @@ + +X [CUSTOM] Custom block +X +X Second custom block line + diff --git a/src/Symfony/Component/Console/Tests/Helper/TableTest.php b/src/Symfony/Component/Console/Tests/Helper/TableTest.php index d917d69591aa8..9ecb381a80090 100644 --- a/src/Symfony/Component/Console/Tests/Helper/TableTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/TableTest.php @@ -269,21 +269,27 @@ public function testRenderProvider() '9971-5-0210-0', new TableCell("A Tale of \nTwo Cities", array('colspan' => 2)), ), + new TableSeparator(), + array( + new TableCell('Cupiditate dicta atque porro, tempora exercitationem modi animi nulla nemo vel nihil!', array('colspan' => 3)), + ), ), 'default', <<getSeverity() : E_ERROR; - if ($this->loggedErrors & $type) { + if (($this->loggedErrors & $type) || $exception instanceof FatalThrowableError) { $e = array( 'type' => $type, 'file' => $exception->getFile(), @@ -521,9 +521,9 @@ public function handleException($exception, array $error = null) } else { $message = 'Uncaught Exception: '.$exception->getMessage(); } - if ($this->loggedErrors & $e['type']) { - $this->loggers[$e['type']][0]->log($this->loggers[$e['type']][1], $message, $e); - } + } + if ($this->loggedErrors & $type) { + $this->loggers[$type][0]->log($this->loggers[$type][1], $message, $e); } if ($exception instanceof FatalErrorException && !$exception instanceof OutOfMemoryException && $error) { foreach ($this->getFatalErrorHandlers() as $handler) { @@ -580,6 +580,8 @@ public static function handleFatalError(array $error = null) } } catch (\Exception $exception) { // Handled below + } catch (\Throwable $exception) { + // Handled below } if ($error && $error['type'] &= E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR) { diff --git a/src/Symfony/Component/Debug/Exception/FatalThrowableError.php b/src/Symfony/Component/Debug/Exception/FatalThrowableError.php index 6ff5ecdaffed0..34f43b17b13b4 100644 --- a/src/Symfony/Component/Debug/Exception/FatalThrowableError.php +++ b/src/Symfony/Component/Debug/Exception/FatalThrowableError.php @@ -27,7 +27,7 @@ public function __construct(\Throwable $e) $message = 'Type error: '.$e->getMessage(); $severity = E_RECOVERABLE_ERROR; } else { - $message = 'Fatal error: '.$e->getMessage(); + $message = $e->getMessage(); $severity = E_ERROR; } diff --git a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php index e2f52fd9d6fb1..163ef530e35d5 100644 --- a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php +++ b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php @@ -448,6 +448,24 @@ public function testHandleFatalError() } } + /** + * @requires PHP 7 + */ + public function testHandleErrorException() + { + $exception = new \Error("Class 'Foo' not found"); + + $handler = new ErrorHandler(); + $handler->setExceptionHandler(function () use (&$args) { + $args = func_get_args(); + }); + + $handler->handleException($exception); + + $this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $args[0]); + $this->assertStringStartsWith("Attempted to load class \"Foo\" from the global namespace.\nDid you forget a \"use\" statement", $args[0]->getMessage()); + } + public function testHandleFatalErrorOnHHVM() { try { diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index e4457d4608451..5065ed1880e87 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -44,8 +44,8 @@ public function process(ContainerBuilder $container) $this->completeDefinition($id, $definition); } } - } catch (\Error $e) { } catch (\Exception $e) { + } catch (\Throwable $e) { } spl_autoload_unregister($throwingAutoloader); @@ -225,13 +225,16 @@ private function set($type, $id) private function createAutowiredDefinition(\ReflectionClass $typeHint, $id) { if (isset($this->notGuessableTypes[$typeHint->name])) { - throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". Several services implementing this type have been declared: "%s".', $typeHint->name, $id, implode('", "', $this->types[$typeHint->name]))); - } + $classOrInterface = $typeHint->isInterface() ? 'interface' : 'class'; + $matchingServices = implode(', ', $this->types[$typeHint->name]); + + throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". Multiple services exist for this %s (%s).', $typeHint->name, $id, $classOrInterface, $matchingServices)); - $noAvailableDefinitionMessage = sprintf('Unable to autowire argument of type "%s" for the service "%s". This type cannot be instantiated automatically and no service implementing this type is declared.', $typeHint->name, $id); + } if (!$typeHint->isInstantiable()) { - throw new RuntimeException($noAvailableDefinitionMessage); + $classOrInterface = $typeHint->isInterface() ? 'interface' : 'class'; + throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". No services were found matching this %s and it cannot be auto-registered.', $typeHint->name, $id, $classOrInterface)); } $argumentId = sprintf('autowired.%s', $typeHint->name); @@ -244,7 +247,9 @@ private function createAutowiredDefinition(\ReflectionClass $typeHint, $id) try { $this->completeDefinition($argumentId, $argumentDefinition); } catch (RuntimeException $e) { - throw new RuntimeException($noAvailableDefinitionMessage, 0, $e); + $classOrInterface = $typeHint->isInterface() ? 'interface' : 'class'; + $message = sprintf('Unable to autowire argument of type "%s" for the service "%s". No services were found matching this %s and it cannot be auto-registered.', $typeHint->name, $id, $classOrInterface); + throw new RuntimeException($message, 0, $e); } return new Reference($argumentId); diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index e0a763dfe8d5b..8ed379ef3c512 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -537,6 +537,10 @@ private function addServiceConfigurator($id, $definition, $variableName = 'insta return sprintf(" %s::%s(\$%s);\n", $this->dumpLiteralClass($class), $callable[1], $variableName); } + if (0 === strpos($class, 'new ')) { + return sprintf(" (%s)->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); + } + return sprintf(" call_user_func(array(%s, '%s'), \$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); } @@ -713,6 +717,10 @@ private function addNewInstance(Definition $definition, $return, $instantiation) return sprintf(" $return{$instantiation}%s::%s(%s);\n", $this->dumpLiteralClass($class), $callable[1], $arguments ? implode(', ', $arguments) : ''); } + if (0 === strpos($class, 'new ')) { + return sprintf(" $return{$instantiation}(%s)->%s(%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? implode(', ', $arguments) : ''); + } + return sprintf(" $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? ', '.implode(', ', $arguments) : ''); } @@ -1158,7 +1166,7 @@ private function hasReference($id, array $arguments, $deep = false, array &$visi return true; } - if ($deep && !isset($visited[$argumentId])) { + if ($deep && !isset($visited[$argumentId]) && 'service_container' !== $argumentId) { $visited[$argumentId] = true; $service = $this->container->getDefinition($argumentId); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index 6098d9a9131c8..b5badc31fdbcd 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -103,7 +103,7 @@ public function testCompleteExistingDefinitionWithNotDefinedArguments() /** * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" for the service "a". Several services implementing this type have been declared: "c1", "c2". + * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" for the service "a". Multiple services exist for this interface (c1, c2, c3). */ public function testTypeCollision() { @@ -111,6 +111,7 @@ public function testTypeCollision() $container->register('c1', __NAMESPACE__.'\CollisionA'); $container->register('c2', __NAMESPACE__.'\CollisionB'); + $container->register('c3', __NAMESPACE__.'\CollisionB'); $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired'); $aDefinition->setAutowired(true); @@ -120,7 +121,7 @@ public function testTypeCollision() /** * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" for the service "a". Several services implementing this type have been declared: "a1", "a2". + * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" for the service "a". Multiple services exist for this class (a1, a2). */ public function testTypeNotGuessable() { @@ -137,7 +138,7 @@ public function testTypeNotGuessable() /** * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\A" for the service "a". Several services implementing this type have been declared: "a1", "a2". + * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\A" for the service "a". Multiple services exist for this class (a1, a2). */ public function testTypeNotGuessableWithSubclass() { @@ -152,6 +153,21 @@ public function testTypeNotGuessableWithSubclass() $pass->process($container); } + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" for the service "a". No services were found matching this interface and it cannot be auto-registered. + */ + public function testTypeNotGuessableNoServicesFound() + { + $container = new ContainerBuilder(); + + $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired'); + $aDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + } + public function testTypeNotGuessableWithTypeSet() { $container = new ContainerBuilder(); @@ -207,21 +223,6 @@ public function testCreateDefinition() $this->assertEquals(__NAMESPACE__.'\Lille', $lilleDefinition->getClass()); } - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" for the service "a". This type cannot be instantiated automatically and no service implementing this type is declared. - */ - public function testCreateNonInstanciable() - { - $container = new ContainerBuilder(); - - $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired'); - $aDefinition->setAutowired(true); - - $pass = new AutowirePass(); - $pass->process($container); - } - public function testResolveParameter() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index d134ccd7f2b4e..c55fe764c2c9d 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -254,4 +254,15 @@ public function testDumpAutowireData() $this->assertEquals(file_get_contents(self::$fixturesPath.'/php/services24.php'), $dumper->dump()); } + + public function testInlinedDefinitionReferencingServiceContainer() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->addMethodCall('add', array(new Reference('service_container')))->setPublic(false); + $container->register('bar', 'stdClass')->addArgument(new Reference('foo')); + $container->compile(); + + $dumper = new PhpDumper($container); + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services13.php', $dumper->dump(), '->dump() dumps inline definitions which reference service_container'); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php index 96f334fdd153f..f499635604bed 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php @@ -78,6 +78,15 @@ ->register('configured_service', 'stdClass') ->setConfigurator(array(new Reference('configurator_service'), 'configureStdClass')) ; +$container + ->register('configurator_service_simple', 'ConfClass') + ->addArgument('bar') + ->setPublic(false) +; +$container + ->register('configured_service_simple', 'stdClass') + ->setConfigurator(array(new Reference('configurator_service_simple'), 'configureStdClass')) +; $container ->register('decorated', 'stdClass') ; @@ -111,5 +120,14 @@ ->register('service_from_static_method', 'Bar\FooClass') ->setFactory(array('Bar\FooClass', 'getInstance')) ; +$container + ->register('factory_simple', 'SimpleFactoryClass') + ->addArgument('foo') + ->setPublic(false) +; +$container + ->register('factory_service_simple', 'Bar') + ->setFactory(array(new Reference('factory_simple'), 'getInstance')) +; return $container; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot index f6536980aa54f..3b24ef8ffbca3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot @@ -14,6 +14,8 @@ digraph sc { node_request [label="request\nRequest\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_configurator_service [label="configurator_service\nConfClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_configured_service [label="configured_service\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_configurator_service_simple [label="configurator_service_simple\nConfClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_configured_service_simple [label="configured_service_simple\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_decorated [label="decorated\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_decorator_service [label="decorator_service\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_decorator_service_with_name [label="decorator_service_with_name\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; @@ -22,6 +24,8 @@ digraph sc { node_factory_service [label="factory_service\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_new_factory_service [label="new_factory_service\nFooBarBaz\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_service_from_static_method [label="service_from_static_method\nBar\\FooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_factory_simple [label="factory_simple\nSimpleFactoryClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_factory_service_simple [label="factory_service_simple\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=record, fillcolor="#9999ff", style="filled"]; node_foo2 [label="foo2\n\n", shape=record, fillcolor="#ff9999", style="filled"]; node_foo3 [label="foo3\n\n", shape=record, fillcolor="#ff9999", style="filled"]; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php new file mode 100644 index 0000000000000..a3dcfc6986df4 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php @@ -0,0 +1,57 @@ +services = array(); + $this->methodMap = array( + 'bar' => 'getBarService', + ); + + $this->aliases = array(); + } + + /** + * {@inheritdoc} + */ + public function compile() + { + throw new LogicException('You cannot compile a dumped frozen container.'); + } + + /** + * Gets the 'bar' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \stdClass A stdClass instance. + */ + protected function getBarService() + { + $a = new \stdClass(); + $a->add($this); + + return $this->services['bar'] = new \stdClass($a); + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php index cc2a18dcd1d57..6c56cfcebf700 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php @@ -40,7 +40,7 @@ public function __construct() */ protected function getServiceFromAnonymousFactoryService() { - return $this->services['service_from_anonymous_factory'] = call_user_func(array(new \Bar\FooClass(), 'getInstance')); + return $this->services['service_from_anonymous_factory'] = (new \Bar\FooClass())->getInstance(); } /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php index a29f7dd768c51..cb84452bcad87 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php @@ -28,12 +28,16 @@ public function __construct() 'bar' => 'getBarService', 'baz' => 'getBazService', 'configurator_service' => 'getConfiguratorServiceService', + 'configurator_service_simple' => 'getConfiguratorServiceSimpleService', 'configured_service' => 'getConfiguredServiceService', + 'configured_service_simple' => 'getConfiguredServiceSimpleService', 'decorated' => 'getDecoratedService', 'decorator_service' => 'getDecoratorServiceService', 'decorator_service_with_name' => 'getDecoratorServiceWithNameService', 'deprecated_service' => 'getDeprecatedServiceService', 'factory_service' => 'getFactoryServiceService', + 'factory_service_simple' => 'getFactoryServiceSimpleService', + 'factory_simple' => 'getFactorySimpleService', 'foo' => 'getFooService', 'foo.baz' => 'getFoo_BazService', 'foo_bar' => 'getFooBarService', @@ -104,6 +108,23 @@ protected function getConfiguredServiceService() return $instance; } + /** + * Gets the 'configured_service_simple' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \stdClass A stdClass instance. + */ + protected function getConfiguredServiceSimpleService() + { + $this->services['configured_service_simple'] = $instance = new \stdClass(); + + $this->get('configurator_service_simple')->configureStdClass($instance); + + return $instance; + } + /** * Gets the 'decorated' service. * @@ -173,6 +194,19 @@ protected function getFactoryServiceService() return $this->services['factory_service'] = $this->get('foo.baz')->getInstance(); } + /** + * Gets the 'factory_service_simple' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Bar A Bar instance. + */ + protected function getFactoryServiceSimpleService() + { + return $this->services['factory_service_simple'] = $this->get('factory_simple')->getInstance(); + } + /** * Gets the 'foo' service. * @@ -334,6 +368,40 @@ protected function getConfiguratorServiceService() return $instance; } + /** + * Gets the 'configurator_service_simple' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * This service is private. + * If you want to be able to request this service from the container directly, + * make it public, otherwise you might end up with broken code. + * + * @return \ConfClass A ConfClass instance. + */ + protected function getConfiguratorServiceSimpleService() + { + return $this->services['configurator_service_simple'] = new \ConfClass('bar'); + } + + /** + * Gets the 'factory_simple' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * This service is private. + * If you want to be able to request this service from the container directly, + * make it public, otherwise you might end up with broken code. + * + * @return \SimpleFactoryClass A SimpleFactoryClass instance. + */ + protected function getFactorySimpleService() + { + return $this->services['factory_simple'] = new \SimpleFactoryClass('foo'); + } + /** * Gets the 'inlined' service. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php index 5386b12a99457..4fd1ac90f3faf 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php @@ -30,10 +30,12 @@ public function __construct() 'bar' => 'getBarService', 'baz' => 'getBazService', 'configured_service' => 'getConfiguredServiceService', + 'configured_service_simple' => 'getConfiguredServiceSimpleService', 'decorator_service' => 'getDecoratorServiceService', 'decorator_service_with_name' => 'getDecoratorServiceWithNameService', 'deprecated_service' => 'getDeprecatedServiceService', 'factory_service' => 'getFactoryServiceService', + 'factory_service_simple' => 'getFactoryServiceSimpleService', 'foo' => 'getFooService', 'foo.baz' => 'getFoo_BazService', 'foo_bar' => 'getFooBarService', @@ -114,6 +116,23 @@ protected function getConfiguredServiceService() return $instance; } + /** + * Gets the 'configured_service_simple' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \stdClass A stdClass instance. + */ + protected function getConfiguredServiceSimpleService() + { + $this->services['configured_service_simple'] = $instance = new \stdClass(); + + (new \ConfClass('bar'))->configureStdClass($instance); + + return $instance; + } + /** * Gets the 'decorator_service' service. * @@ -170,6 +189,19 @@ protected function getFactoryServiceService() return $this->services['factory_service'] = $this->get('foo.baz')->getInstance(); } + /** + * Gets the 'factory_service_simple' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Bar A Bar instance. + */ + protected function getFactoryServiceSimpleService() + { + return $this->services['factory_service_simple'] = (new \SimpleFactoryClass('foo'))->getInstance(); + } + /** * Gets the 'foo' service. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml index 4ddb8655c55f2..d3974c07b01a7 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml @@ -84,6 +84,12 @@ + + bar + + + + @@ -103,6 +109,12 @@ + + foo + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml index 6efeb37e747d4..4a97dcadffa66 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml @@ -67,6 +67,13 @@ services: configured_service: class: stdClass configurator: ['@configurator_service', configureStdClass] + configurator_service_simple: + class: ConfClass + public: false + arguments: ['bar'] + configured_service_simple: + class: stdClass + configurator: ['@configurator_service_simple', configureStdClass] decorated: class: stdClass decorator_service: @@ -93,5 +100,12 @@ services: service_from_static_method: class: Bar\FooClass factory: [Bar\FooClass, getInstance] + factory_simple: + class: SimpleFactoryClass + public: false + arguments: ['foo'] + factory_service_simple: + class: Bar + factory: ['@factory_simple', getInstance] alias_for_foo: '@foo' alias_for_alias: '@foo' diff --git a/src/Symfony/Component/EventDispatcher/GenericEvent.php b/src/Symfony/Component/EventDispatcher/GenericEvent.php index 6458180a5aac3..03cbcfe33414e 100644 --- a/src/Symfony/Component/EventDispatcher/GenericEvent.php +++ b/src/Symfony/Component/EventDispatcher/GenericEvent.php @@ -61,9 +61,9 @@ public function getSubject() * * @param string $key Key. * - * @throws \InvalidArgumentException If key is not found. - * * @return mixed Contents of array key. + * + * @throws \InvalidArgumentException If key is not found. */ public function getArgument($key) { @@ -130,9 +130,9 @@ public function hasArgument($key) * * @param string $key Array key. * - * @throws \InvalidArgumentException If key does not exist in $this->args. - * * @return mixed + * + * @throws \InvalidArgumentException If key does not exist in $this->args. */ public function offsetGet($key) { diff --git a/src/Symfony/Component/Finder/Finder.php b/src/Symfony/Component/Finder/Finder.php index a8543cfdad5b8..6862fd7c2875a 100644 --- a/src/Symfony/Component/Finder/Finder.php +++ b/src/Symfony/Component/Finder/Finder.php @@ -82,7 +82,7 @@ public static function create() /** * Restricts the matching to directories only. * - * @return Finder The current Finder instance + * @return Finder|SplFileInfo[] The current Finder instance */ public function directories() { @@ -94,7 +94,7 @@ public function directories() /** * Restricts the matching to files only. * - * @return Finder The current Finder instance + * @return Finder|SplFileInfo[] The current Finder instance */ public function files() { @@ -113,7 +113,7 @@ public function files() * * @param int $level The depth level expression * - * @return Finder The current Finder instance + * @return Finder|SplFileInfo[] The current Finder instance * * @see DepthRangeFilterIterator * @see NumberComparator @@ -137,7 +137,7 @@ public function depth($level) * * @param string $date A date range string * - * @return Finder The current Finder instance + * @return Finder|SplFileInfo[] The current Finder instance * * @see strtotime * @see DateRangeFilterIterator @@ -161,7 +161,7 @@ public function date($date) * * @param string $pattern A pattern (a regexp, a glob, or a string) * - * @return Finder The current Finder instance + * @return Finder|SplFileInfo[] The current Finder instance * * @see FilenameFilterIterator */ @@ -177,7 +177,7 @@ public function name($pattern) * * @param string $pattern A pattern (a regexp, a glob, or a string) * - * @return Finder The current Finder instance + * @return Finder|SplFileInfo[] The current Finder instance * * @see FilenameFilterIterator */ @@ -198,7 +198,7 @@ public function notName($pattern) * * @param string $pattern A pattern (string or regexp) * - * @return Finder The current Finder instance + * @return Finder|SplFileInfo[] The current Finder instance * * @see FilecontentFilterIterator */ @@ -219,7 +219,7 @@ public function contains($pattern) * * @param string $pattern A pattern (string or regexp) * - * @return Finder The current Finder instance + * @return Finder|SplFileInfo[] The current Finder instance * * @see FilecontentFilterIterator */ @@ -242,7 +242,7 @@ public function notContains($pattern) * * @param string $pattern A pattern (a regexp or a string) * - * @return Finder The current Finder instance + * @return Finder|SplFileInfo[] The current Finder instance * * @see FilenameFilterIterator */ @@ -265,7 +265,7 @@ public function path($pattern) * * @param string $pattern A pattern (a regexp or a string) * - * @return Finder The current Finder instance + * @return Finder|SplFileInfo[] The current Finder instance * * @see FilenameFilterIterator */ @@ -285,7 +285,7 @@ public function notPath($pattern) * * @param string $size A size range string * - * @return Finder The current Finder instance + * @return Finder|SplFileInfo[] The current Finder instance * * @see SizeRangeFilterIterator * @see NumberComparator @@ -302,7 +302,7 @@ public function size($size) * * @param string|array $dirs A directory path or an array of directories * - * @return Finder The current Finder instance + * @return Finder|SplFileInfo[] The current Finder instance * * @see ExcludeDirectoryFilterIterator */ @@ -318,7 +318,7 @@ public function exclude($dirs) * * @param bool $ignoreDotFiles Whether to exclude "hidden" files or not * - * @return Finder The current Finder instance + * @return Finder|SplFileInfo[] The current Finder instance * * @see ExcludeDirectoryFilterIterator */ @@ -338,7 +338,7 @@ public function ignoreDotFiles($ignoreDotFiles) * * @param bool $ignoreVCS Whether to exclude VCS files or not * - * @return Finder The current Finder instance + * @return Finder|SplFileInfo[] The current Finder instance * * @see ExcludeDirectoryFilterIterator */ @@ -378,7 +378,7 @@ public static function addVCSPattern($pattern) * * @param \Closure $closure An anonymous function * - * @return Finder The current Finder instance + * @return Finder|SplFileInfo[] The current Finder instance * * @see SortableIterator */ @@ -394,7 +394,7 @@ public function sort(\Closure $closure) * * This can be slow as all the matching files and directories must be retrieved for comparison. * - * @return Finder The current Finder instance + * @return Finder|SplFileInfo[] The current Finder instance * * @see SortableIterator */ @@ -410,7 +410,7 @@ public function sortByName() * * This can be slow as all the matching files and directories must be retrieved for comparison. * - * @return Finder The current Finder instance + * @return Finder|SplFileInfo[] The current Finder instance * * @see SortableIterator */ @@ -428,7 +428,7 @@ public function sortByType() * * This can be slow as all the matching files and directories must be retrieved for comparison. * - * @return Finder The current Finder instance + * @return Finder|SplFileInfo[] The current Finder instance * * @see SortableIterator */ @@ -448,7 +448,7 @@ public function sortByAccessedTime() * * This can be slow as all the matching files and directories must be retrieved for comparison. * - * @return Finder The current Finder instance + * @return Finder|SplFileInfo[] The current Finder instance * * @see SortableIterator */ @@ -466,7 +466,7 @@ public function sortByChangedTime() * * This can be slow as all the matching files and directories must be retrieved for comparison. * - * @return Finder The current Finder instance + * @return Finder|SplFileInfo[] The current Finder instance * * @see SortableIterator */ @@ -485,7 +485,7 @@ public function sortByModifiedTime() * * @param \Closure $closure An anonymous function * - * @return Finder The current Finder instance + * @return Finder|SplFileInfo[] The current Finder instance * * @see CustomFilterIterator */ @@ -499,7 +499,7 @@ public function filter(\Closure $closure) /** * Forces the following of symlinks. * - * @return Finder The current Finder instance + * @return Finder|SplFileInfo[] The current Finder instance */ public function followLinks() { @@ -515,7 +515,7 @@ public function followLinks() * * @param bool $ignore * - * @return Finder The current Finder instance + * @return Finder|SplFileInfo[] The current Finder instance */ public function ignoreUnreadableDirs($ignore = true) { @@ -529,7 +529,7 @@ public function ignoreUnreadableDirs($ignore = true) * * @param string|array $dirs A directory path or an array of directories * - * @return Finder The current Finder instance + * @return Finder|SplFileInfo[] The current Finder instance * * @throws \InvalidArgumentException if one of the directories does not exist */ @@ -590,7 +590,7 @@ public function getIterator() * * @param mixed $iterator * - * @return Finder The finder + * @return Finder|SplFileInfo[] The finder * * @throws \InvalidArgumentException When the given argument is not iterable. */ diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php index 07070952f5065..614656b45d504 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php @@ -72,8 +72,8 @@ public function transform($dateTime) ), array_flip($this->fields)); } - if (!$dateTime instanceof \DateTime && !$dateTime instanceof \DateTimeInterface) { - throw new TransformationFailedException('Expected a \DateTime or \DateTimeInterface.'); + if (!$dateTime instanceof \DateTimeInterface) { + throw new TransformationFailedException('Expected a \DateTimeInterface.'); } if ($this->inputTimezone !== $this->outputTimezone) { diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php index b38680a1bb6ec..2e6df405d5b6e 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php @@ -84,8 +84,8 @@ public function transform($dateTime) return ''; } - if (!$dateTime instanceof \DateTime && !$dateTime instanceof \DateTimeInterface) { - throw new TransformationFailedException('Expected a \DateTime or \DateTimeInterface.'); + if (!$dateTime instanceof \DateTimeInterface) { + throw new TransformationFailedException('Expected a \DateTimeInterface.'); } $value = $this->getIntlDateFormatter()->format($dateTime->getTimestamp()); @@ -126,7 +126,7 @@ public function reverseTransform($value) try { // read timestamp into DateTime object - the formatter delivers in UTC - $dateTime = new \DateTime(sprintf('@%s UTC', $timestamp)); + $dateTime = new \DateTime(sprintf('@%s', $timestamp)); } catch (\Exception $e) { throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); } diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToRfc3339Transformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToRfc3339Transformer.php index c78d80401a385..3ebc86de7bb86 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToRfc3339Transformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToRfc3339Transformer.php @@ -27,8 +27,8 @@ public function transform($dateTime) return ''; } - if (!$dateTime instanceof \DateTime && !$dateTime instanceof \DateTimeInterface) { - throw new TransformationFailedException('Expected a \DateTime or \DateTimeInterface.'); + if (!$dateTime instanceof \DateTimeInterface) { + throw new TransformationFailedException('Expected a \DateTimeInterface.'); } if ($this->inputTimezone !== $this->outputTimezone) { diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php index 7e2d3beece6ef..d855122b4159b 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php @@ -99,8 +99,8 @@ public function transform($value) return ''; } - if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) { - throw new TransformationFailedException('Expected a \DateTime or \DateTimeInterface.'); + if (!$value instanceof \DateTimeInterface) { + throw new TransformationFailedException('Expected a \DateTimeInterface.'); } if (!$value instanceof \DateTimeImmutable) { diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToTimestampTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToTimestampTransformer.php index b8f5f381a5a40..031c0c3a111f1 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToTimestampTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToTimestampTransformer.php @@ -38,8 +38,8 @@ public function transform($value) return; } - if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) { - throw new TransformationFailedException('Expected a \DateTime or \DateTimeInterface.'); + if (!$value instanceof \DateTimeInterface) { + throw new TransformationFailedException('Expected a \DateTimeInterface.'); } return $value->getTimestamp(); diff --git a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php index d3ba9cf7bf23b..304ec17e1a493 100644 --- a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php +++ b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php @@ -162,9 +162,7 @@ private function matchChild(FormInterface $form, PropertyPathIteratorInterface $ } } - $children = iterator_to_array(new \RecursiveIteratorIterator( - new InheritDataAwareIterator($form) - )); + $children = iterator_to_array(new \RecursiveIteratorIterator(new InheritDataAwareIterator($form)), false); while ($it->valid()) { if ($it->isIndex()) { @@ -189,7 +187,7 @@ private function matchChild(FormInterface $form, PropertyPathIteratorInterface $ } /** @var FormInterface $child */ - foreach ($children as $key => $child) { + foreach ($children as $i => $child) { $childPath = (string) $child->getPropertyPath(); if ($childPath === $chunk) { $target = $child; @@ -198,7 +196,7 @@ private function matchChild(FormInterface $form, PropertyPathIteratorInterface $ continue; } - unset($children[$key]); + unset($children[$i]); } $it->next(); diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php index f306e11051043..74ff45b297dc2 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -1018,9 +1018,9 @@ public function createView(FormView $parent = null) * * @param mixed $value The value to transform * - * @throws TransformationFailedException If the value cannot be transformed to "normalized" format - * * @return mixed + * + * @throws TransformationFailedException If the value cannot be transformed to "normalized" format */ private function modelToNorm($value) { @@ -1044,9 +1044,9 @@ private function modelToNorm($value) * * @param string $value The value to reverse transform * - * @throws TransformationFailedException If the value cannot be transformed to "model" format - * * @return mixed + * + * @throws TransformationFailedException If the value cannot be transformed to "model" format */ private function normToModel($value) { @@ -1072,9 +1072,9 @@ private function normToModel($value) * * @param mixed $value The value to transform * - * @throws TransformationFailedException If the value cannot be transformed to "view" format - * * @return mixed + * + * @throws TransformationFailedException If the value cannot be transformed to "view" format */ private function normToView($value) { @@ -1107,9 +1107,9 @@ private function normToView($value) * * @param string $value The value to reverse transform * - * @throws TransformationFailedException If the value cannot be transformed to "normalized" format - * * @return mixed + * + * @throws TransformationFailedException If the value cannot be transformed to "normalized" format */ private function viewToNorm($value) { diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php index a099e104595ff..74da4aee1c281 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php @@ -1542,24 +1542,39 @@ public function testErrorMappingForFormInheritingParentData($target, $childName, public function testBacktrackIfSeveralSubFormsWithSamePropertyPath() { - $violation = $this->getConstraintViolation('data.address[street]'); $parent = $this->getForm('parent'); $child1 = $this->getForm('subform1', 'address'); $child2 = $this->getForm('subform2', 'address'); - $grandChild = $this->getForm('street'); + $child3 = $this->getForm('subform3', null, null, array(), true); + $child4 = $this->getForm('subform4', null, null, array(), true); + $grandChild1 = $this->getForm('street'); + $grandChild2 = $this->getForm('street', '[sub_address1_street]'); + $grandChild3 = $this->getForm('street', '[sub_address2_street]'); $parent->add($child1); $parent->add($child2); - $child2->add($grandChild); + $parent->add($child3); + $parent->add($child4); + $child2->add($grandChild1); + $child3->add($grandChild2); + $child4->add($grandChild3); $parent->submit(array()); - $this->mapper->mapViolation($violation, $parent); + $violation1 = $this->getConstraintViolation('data.address[street]'); + $violation2 = $this->getConstraintViolation('data[sub_address1_street]'); + $violation3 = $this->getConstraintViolation('data[sub_address2_street]'); + $this->mapper->mapViolation($violation1, $parent); + $this->mapper->mapViolation($violation2, $parent); + $this->mapper->mapViolation($violation3, $parent); - // The error occurred on the child of the second form with the same path $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); $this->assertCount(0, $child1->getErrors(), $child1->getName().' should not have an error, but has one'); $this->assertCount(0, $child2->getErrors(), $child2->getName().' should not have an error, but has one'); - $this->assertEquals(array($this->getFormError($violation, $grandChild)), iterator_to_array($grandChild->getErrors()), $grandChild->getName().' should have an error, but has none'); + $this->assertCount(0, $child3->getErrors(), $child3->getName().' should not have an error, but has one'); + $this->assertCount(0, $child4->getErrors(), $child4->getName().' should not have an error, but has one'); + $this->assertEquals(array($this->getFormError($violation1, $grandChild1)), iterator_to_array($grandChild1->getErrors()), $grandChild1->getName().' should have an error, but has none'); + $this->assertEquals(array($this->getFormError($violation2, $grandChild2)), iterator_to_array($grandChild2->getErrors()), $grandChild2->getName().' should have an error, but has none'); + $this->assertEquals(array($this->getFormError($violation3, $grandChild3)), iterator_to_array($grandChild3->getErrors()), $grandChild3->getName().' should have an error, but has none'); } } diff --git a/src/Symfony/Component/HttpFoundation/Cookie.php b/src/Symfony/Component/HttpFoundation/Cookie.php index 13d69f3bd2edd..29ff700fa841a 100644 --- a/src/Symfony/Component/HttpFoundation/Cookie.php +++ b/src/Symfony/Component/HttpFoundation/Cookie.php @@ -51,7 +51,7 @@ public function __construct($name, $value = null, $expire = 0, $path = '/', $dom } // convert expiration time to a Unix timestamp - if ($expire instanceof \DateTime || $expire instanceof \DateTimeInterface) { + if ($expire instanceof \DateTimeInterface) { $expire = $expire->format('U'); } elseif (!is_numeric($expire)) { $expire = strtotime($expire); diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index 7ef986ea061d2..12b8db98b4a94 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -59,6 +59,7 @@ class Response const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416; const HTTP_EXPECTATION_FAILED = 417; const HTTP_I_AM_A_TEAPOT = 418; // RFC2324 + const HTTP_MISDIRECTED_REQUEST = 421; // RFC7540 const HTTP_UNPROCESSABLE_ENTITY = 422; // RFC4918 const HTTP_LOCKED = 423; // RFC4918 const HTTP_FAILED_DEPENDENCY = 424; // RFC4918 @@ -115,7 +116,7 @@ class Response * * The list of codes is complete according to the * {@link http://www.iana.org/assignments/http-status-codes/ Hypertext Transfer Protocol (HTTP) Status Code Registry} - * (last updated 2015-05-19). + * (last updated 2016-03-01). * * Unless otherwise noted, the status code is defined in RFC2616. * @@ -162,6 +163,7 @@ class Response 416 => 'Range Not Satisfiable', 417 => 'Expectation Failed', 418 => 'I\'m a teapot', // RFC2324 + 421 => 'Misdirected Request', // RFC7540 422 => 'Unprocessable Entity', // RFC4918 423 => 'Locked', // RFC4918 424 => 'Failed Dependency', // RFC4918 diff --git a/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php b/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php index 06534aa75bf39..1d26e86b9b17b 100644 --- a/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php +++ b/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php @@ -181,9 +181,9 @@ public function removeCookie($name, $path = '/', $domain = null) * * @param string $format * - * @throws \InvalidArgumentException When the $format is invalid - * * @return array + * + * @throws \InvalidArgumentException When the $format is invalid */ public function getCookies($format = self::COOKIES_FLAT) { diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php b/src/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php index 4b2f5d27aab41..206f18e77f8d9 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php @@ -24,9 +24,9 @@ interface SessionStorageInterface /** * Starts the session. * - * @throws \RuntimeException If something goes wrong starting the session. - * * @return bool True if started. + * + * @throws \RuntimeException If something goes wrong starting the session. */ public function start(); diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php index e8795244b176e..0c579d7724846 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php @@ -30,6 +30,10 @@ class MemcacheSessionHandlerTest extends \PHPUnit_Framework_TestCase protected function setUp() { + if (defined('HHVM_VERSION')) { + $this->markTestSkipped('PHPUnit_MockObject cannot mock the Memcache class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289'); + } + parent::setUp(); $this->memcache = $this->getMock('Memcache'); $this->storage = new MemcacheSessionHandler( diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php index f51ec8ed63c86..75e4a20e2a8d5 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php @@ -31,6 +31,10 @@ class MemcachedSessionHandlerTest extends \PHPUnit_Framework_TestCase protected function setUp() { + if (defined('HHVM_VERSION')) { + $this->markTestSkipped('PHPUnit_MockObject cannot mock the Memcached class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289'); + } + parent::setUp(); if (version_compare(phpversion('memcached'), '2.2.0', '>=')) { diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php index 1af321096ebad..9612320976944 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php @@ -135,6 +135,10 @@ public function testReadWriteReadWithNullByte() public function testReadConvertsStreamToString() { + if (defined('HHVM_VERSION')) { + $this->markTestSkipped('PHPUnit_MockObject cannot mock the PDOStatement class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289'); + } + $pdo = new MockPdo('pgsql'); $pdo->prepareResult = $this->getMock('PDOStatement'); @@ -152,6 +156,10 @@ public function testReadConvertsStreamToString() public function testReadLockedConvertsStreamToString() { + if (defined('HHVM_VERSION')) { + $this->markTestSkipped('PHPUnit_MockObject cannot mock the PDOStatement class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289'); + } + $pdo = new MockPdo('pgsql'); $selectStmt = $this->getMock('PDOStatement'); $insertStmt = $this->getMock('PDOStatement'); diff --git a/src/Symfony/Component/HttpKernel/DataCollector/Util/ValueExporter.php b/src/Symfony/Component/HttpKernel/DataCollector/Util/ValueExporter.php index c9e51cc26ff12..b06dd83b3da39 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/Util/ValueExporter.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/Util/ValueExporter.php @@ -28,7 +28,7 @@ class ValueExporter public function exportValue($value, $depth = 1, $deep = false) { if (is_object($value)) { - if ($value instanceof \DateTime || $value instanceof \DateTimeInterface) { + if ($value instanceof \DateTimeInterface) { return sprintf('Object(%s) - %s', get_class($value), $value->format(\DateTime::ISO8601)); } diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php index 546e3ca2b3364..1c7684a91b78c 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php @@ -154,6 +154,8 @@ public function getKernel() * Gets the Surrogate instance. * * @return SurrogateInterface A Surrogate instance + * + * @throws \LogicException */ public function getSurrogate() { diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 8dde0e1d8ed3e..ddff065a23779 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -59,11 +59,11 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '3.0.6'; - const VERSION_ID = 30006; + const VERSION = '3.0.7'; + const VERSION_ID = 30007; const MAJOR_VERSION = 3; const MINOR_VERSION = 0; - const RELEASE_VERSION = 6; + const RELEASE_VERSION = 7; const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '07/2016'; diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php index 7ba1ff6e451d4..c591d59f3bd62 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php @@ -128,7 +128,7 @@ public function getUndefinedControllers() return array( array(1, 'InvalidArgumentException', 'Unable to find controller "1".'), array('foo', 'InvalidArgumentException', 'Unable to find controller "foo".'), - array('foo::bar', 'InvalidArgumentException', 'Class "foo" does not exist.'), + array('oof::bar', 'InvalidArgumentException', 'Class "oof" does not exist.'), array('stdClass', 'InvalidArgumentException', 'Unable to find controller "stdClass".'), array('Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::staticsAction', 'InvalidArgumentException', 'The controller for URI "/" is not callable. Expected method "staticsAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest", did you mean "staticAction"?'), array('Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::privateAction', 'InvalidArgumentException', 'The controller for URI "/" is not callable. Method "privateAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'), diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php index a607d279dbca4..15b77e0c5be12 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php @@ -113,7 +113,7 @@ public function testFlush() $line = __LINE__ - 1; ob_start(); - $collector = null; + $collector->__destruct(); $this->assertSame("DumpDataCollectorTest.php on line {$line}:\n456\n", ob_get_clean()); } } diff --git a/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php b/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php index 1e824d7eed844..0439e2b5d12a1 100644 --- a/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php +++ b/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php @@ -328,7 +328,7 @@ public static function create($locale = 'en', $style = null, $pattern = null) * @return string The formatted currency value * * @see http://www.php.net/manual/en/numberformatter.formatcurrency.php - * @see http://www.iso.org/iso/support/faqs/faqs_widely_used_standards/widely_used_standards_other/currency_codes/currency_codes_list-1.htm + * @see https://en.wikipedia.org/wiki/ISO_4217#Active_codes */ public function formatCurrency($value, $currency) { diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index 5b2004c65c6e6..0e99a7e421fe7 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -477,10 +477,10 @@ public function getOutput() * In comparison with the getOutput method which always return the whole * output, this one returns the new output since the last call. * + * @return string The process output since the last call + * * @throws LogicException in case the output has been disabled * @throws LogicException In case the process is not started - * - * @return string The process output since the last call */ public function getIncrementalOutput() { @@ -536,10 +536,10 @@ public function getErrorOutput() * whole error output, this one returns the new error output since the last * call. * + * @return string The process error output since the last call + * * @throws LogicException in case the output has been disabled * @throws LogicException In case the process is not started - * - * @return string The process error output since the last call */ public function getIncrementalErrorOutput() { diff --git a/src/Symfony/Component/PropertyAccess/StringUtil.php b/src/Symfony/Component/PropertyAccess/StringUtil.php index 248a6483d8b0a..caa8c3d7e1a0d 100644 --- a/src/Symfony/Component/PropertyAccess/StringUtil.php +++ b/src/Symfony/Component/PropertyAccess/StringUtil.php @@ -124,6 +124,9 @@ class StringUtil // chateaux (chateau) array('xuae', 4, false, true, 'eau'), + + // people (person) + array('elpoep', 6, true, true, 'person'), ); /** diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php index f3c7b405c1758..3f1ef1c040a6a 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -242,7 +242,9 @@ public function testSetValueThrowsNoExceptionIfIndexNotFoundAndIndexExceptionsEn */ public function testSetValueThrowsExceptionIfNotArrayAccess() { - $this->propertyAccessor->setValue(new \stdClass(), '[index]', 'Updated'); + $object = new \stdClass(); + + $this->propertyAccessor->setValue($object, '[index]', 'Updated'); } public function testSetValueUpdatesMagicSet() @@ -259,7 +261,9 @@ public function testSetValueUpdatesMagicSet() */ public function testSetValueThrowsExceptionIfThereAreMissingParameters() { - $this->propertyAccessor->setValue(new TestClass('Bernhard'), 'publicAccessorWithMoreRequiredParameters', 'Updated'); + $object = new TestClass('Bernhard'); + + $this->propertyAccessor->setValue($object, 'publicAccessorWithMoreRequiredParameters', 'Updated'); } /** @@ -527,7 +531,9 @@ public function testIsWritableForReferenceChainIssue($object, $path, $value) */ public function testThrowTypeError() { - $this->propertyAccessor->setValue(new TypeHinted(), 'date', 'This is a string, \DateTime expected.'); + $object = new TypeHinted(); + + $this->propertyAccessor->setValue($object, 'date', 'This is a string, \DateTime expected.'); } public function testSetTypeHint() diff --git a/src/Symfony/Component/PropertyAccess/Tests/StringUtilTest.php b/src/Symfony/Component/PropertyAccess/Tests/StringUtilTest.php index 0fd6bb69b2956..983e355cb9038 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/StringUtilTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/StringUtilTest.php @@ -107,6 +107,8 @@ public function singularifyProvider() array('objectives', 'objective'), array('oxen', 'ox'), array('parties', 'party'), + array('people', 'person'), + array('persons', 'person'), array('phenomena', array('phenomenon', 'phenomenum')), array('photos', 'photo'), array('pianos', 'piano'), diff --git a/src/Symfony/Component/Routing/Loader/AnnotationFileLoader.php b/src/Symfony/Component/Routing/Loader/AnnotationFileLoader.php index a3a7e0e5ae50e..b8fc03615f968 100644 --- a/src/Symfony/Component/Routing/Loader/AnnotationFileLoader.php +++ b/src/Symfony/Component/Routing/Loader/AnnotationFileLoader.php @@ -112,7 +112,24 @@ protected function findClass($file) } if (T_CLASS === $token[0]) { - $class = true; + // Skip usage of ::class constant + $isClassConstant = false; + for ($j = $i - 1; $j > 0; --$j) { + if (!isset($tokens[$j][1])) { + break; + } + + if (T_DOUBLE_COLON === $tokens[$j][0]) { + $isClassConstant = true; + break; + } elseif (!in_array($tokens[$j][0], array(T_WHITESPACE, T_DOC_COMMENT, T_COMMENT))) { + break; + } + } + + if (!$isClassConstant) { + $class = true; + } } if (T_NAMESPACE === $token[0]) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/FooTrait.php b/src/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/FooTrait.php new file mode 100644 index 0000000000000..ee8f4b071a368 --- /dev/null +++ b/src/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/FooTrait.php @@ -0,0 +1,13 @@ +loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/FooClass.php'); } + /** + * @requires PHP 5.4 + */ + public function testLoadTraitWithClassConstant() + { + $this->reader->expects($this->never())->method('getClassAnnotation'); + + $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/FooTrait.php'); + } + /** * @requires PHP 5.6 */ diff --git a/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php b/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php index f8f17b4f723e6..352ba584a7638 100644 --- a/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php +++ b/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php @@ -59,7 +59,7 @@ public function supportsDecoding($format) * * @return DecoderInterface * - * @throws RuntimeException if no decoder is found + * @throws RuntimeException If no decoder is found. */ private function getDecoder($format) { diff --git a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php index d1f09f6eaf94e..4c17fb6b8d859 100644 --- a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php +++ b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php @@ -461,6 +461,8 @@ private function needsCdataWrapping($val) * @param mixed $val * * @return bool + * + * @throws UnexpectedValueException */ private function selectNodeType(\DOMNode $node, $val) { diff --git a/src/Symfony/Component/Serializer/Normalizer/DenormalizableInterface.php b/src/Symfony/Component/Serializer/Normalizer/DenormalizableInterface.php index 86c270299ee47..8ad1386a6cbd4 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DenormalizableInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/DenormalizableInterface.php @@ -33,6 +33,8 @@ interface DenormalizableInterface * @param string|null $format The format is optionally given to be able to denormalize differently * based on different input formats * @param array $context options for denormalizing + * + * @return object */ public function denormalize(DenormalizerInterface $denormalizer, $data, $format = null, array $context = array()); } diff --git a/src/Symfony/Component/Serializer/Normalizer/NormalizableInterface.php b/src/Symfony/Component/Serializer/Normalizer/NormalizableInterface.php index b9fefe887a44a..e19fe5ce58576 100644 --- a/src/Symfony/Component/Serializer/Normalizer/NormalizableInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/NormalizableInterface.php @@ -33,7 +33,7 @@ interface NormalizableInterface * based on different output formats. * @param array $context Options for normalizing this object * - * @return array|string|bool|int|float|null + * @return array|scalar */ public function normalize(NormalizerInterface $normalizer, $format = null, array $context = array()); } diff --git a/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php b/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php index 2a51d631b1624..f4bd355232b0b 100644 --- a/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php @@ -25,7 +25,7 @@ interface NormalizerInterface * @param string $format format the normalization result will be encoded as * @param array $context Context options for the normalizer * - * @return array|string|bool|int|float|null + * @return array|scalar */ public function normalize($object, $format = null, array $context = array()); diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 69b6d0bd8e9d9..5cd909c13238e 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -308,6 +308,19 @@ public function testIgnoredAttributes() ); } + public function testIgnoredAttributesDenormalize() + { + $this->normalizer->setIgnoredAttributes(array('fooBar', 'bar', 'baz')); + + $obj = new ObjectDummy(); + $obj->setFoo('foo'); + + $this->assertEquals( + $obj, + $this->normalizer->denormalize(array('fooBar' => 'fooBar', 'foo' => 'foo', 'baz' => 'baz'), __NAMESPACE__.'\ObjectDummy') + ); + } + public function provideCallbacks() { return array( diff --git a/src/Symfony/Component/Stopwatch/StopwatchEvent.php b/src/Symfony/Component/Stopwatch/StopwatchEvent.php index 930722d2f598c..957a5d0dae0a8 100644 --- a/src/Symfony/Component/Stopwatch/StopwatchEvent.php +++ b/src/Symfony/Component/Stopwatch/StopwatchEvent.php @@ -87,8 +87,6 @@ public function start() /** * Stops the last started event period. * - * @throws \LogicException When start wasn't called before stopping - * * @return StopwatchEvent The event * * @throws \LogicException When stop() is called without a matching call to start() diff --git a/src/Symfony/Component/Translation/Extractor/AbstractFileExtractor.php b/src/Symfony/Component/Translation/Extractor/AbstractFileExtractor.php index 196bc3341060a..57fd4938d22cf 100644 --- a/src/Symfony/Component/Translation/Extractor/AbstractFileExtractor.php +++ b/src/Symfony/Component/Translation/Extractor/AbstractFileExtractor.php @@ -54,9 +54,9 @@ private function toSplFileInfo($file) /** * @param string $file * - * @throws \InvalidArgumentException - * * @return bool + * + * @throws \InvalidArgumentException */ protected function isFile($file) { diff --git a/src/Symfony/Component/Translation/Loader/XliffFileLoader.php b/src/Symfony/Component/Translation/Loader/XliffFileLoader.php index bdca11099d704..4a52c0cc6ea16 100644 --- a/src/Symfony/Component/Translation/Loader/XliffFileLoader.php +++ b/src/Symfony/Component/Translation/Loader/XliffFileLoader.php @@ -160,6 +160,8 @@ private function utf8ToCharset($content, $encoding = null) } /** + * Validates and parses the given file into a DOMDocument. + * * @param string $file * @param \DOMDocument $dom * @param string $schema source of the schema diff --git a/src/Symfony/Component/Translation/TranslatorBagInterface.php b/src/Symfony/Component/Translation/TranslatorBagInterface.php index 6f650b5ee0f88..14fbb17f5e46e 100644 --- a/src/Symfony/Component/Translation/TranslatorBagInterface.php +++ b/src/Symfony/Component/Translation/TranslatorBagInterface.php @@ -23,9 +23,9 @@ interface TranslatorBagInterface * * @param string|null $locale The locale or null to use the default * - * @throws \InvalidArgumentException If the locale contains invalid characters - * * @return MessageCatalogueInterface + * + * @throws \InvalidArgumentException If the locale contains invalid characters */ public function getCatalogue($locale = null); } diff --git a/src/Symfony/Component/Translation/TranslatorInterface.php b/src/Symfony/Component/Translation/TranslatorInterface.php index 869e0b900442d..6916c335bd426 100644 --- a/src/Symfony/Component/Translation/TranslatorInterface.php +++ b/src/Symfony/Component/Translation/TranslatorInterface.php @@ -26,9 +26,9 @@ interface TranslatorInterface * @param string|null $domain The domain for the message or null to use the default * @param string|null $locale The locale or null to use the default * - * @throws \InvalidArgumentException If the locale contains invalid characters - * * @return string The translated string + * + * @throws \InvalidArgumentException If the locale contains invalid characters */ public function trans($id, array $parameters = array(), $domain = null, $locale = null); @@ -41,9 +41,9 @@ public function trans($id, array $parameters = array(), $domain = null, $locale * @param string|null $domain The domain for the message or null to use the default * @param string|null $locale The locale or null to use the default * - * @throws \InvalidArgumentException If the locale contains invalid characters - * * @return string The translated string + * + * @throws \InvalidArgumentException If the locale contains invalid characters */ public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null); diff --git a/src/Symfony/Component/Validator/ConstraintValidator.php b/src/Symfony/Component/Validator/ConstraintValidator.php index bd69ee92a9680..ece4400065c6a 100644 --- a/src/Symfony/Component/Validator/ConstraintValidator.php +++ b/src/Symfony/Component/Validator/ConstraintValidator.php @@ -72,7 +72,7 @@ protected function formatTypeOf($value) * (i.e. "false" for false, "1" for 1 etc.). Strings are always wrapped * in double quotes ("). Objects, arrays and resources are formatted as * "object", "array" and "resource". If the $format bitmask contains - * the PRETTY_DATE bit, then {@link \DateTime} objects will be formatted + * the PRETTY_DATE bit, then {@link \DateTime} objects will be formatted * as RFC-3339 dates ("Y-m-d H:i:s"). * * Be careful when passing message parameters to a constraint violation @@ -89,7 +89,7 @@ protected function formatTypeOf($value) */ protected function formatValue($value, $format = 0) { - $isDateTime = $value instanceof \DateTime || $value instanceof \DateTimeInterface; + $isDateTime = $value instanceof \DateTimeInterface; if (($format & self::PRETTY_DATE) && $isDateTime) { if (class_exists('IntlDateFormatter')) { diff --git a/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php b/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php index 85a3f944be5d6..024e6698a57e4 100644 --- a/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php +++ b/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php @@ -47,7 +47,7 @@ public function validate($value, Constraint $constraint) // If $value is immutable, convert the compared value to a // DateTimeImmutable too $comparedValue = new \DatetimeImmutable($comparedValue); - } elseif ($value instanceof \DateTime || $value instanceof \DateTimeInterface) { + } elseif ($value instanceof \DateTimeInterface) { // Otherwise use DateTime $comparedValue = new \DateTime($comparedValue); } diff --git a/src/Symfony/Component/Validator/Constraints/DateTimeValidator.php b/src/Symfony/Component/Validator/Constraints/DateTimeValidator.php index 8d82bd8ca9a4b..0a867a7d4b25b 100644 --- a/src/Symfony/Component/Validator/Constraints/DateTimeValidator.php +++ b/src/Symfony/Component/Validator/Constraints/DateTimeValidator.php @@ -30,7 +30,7 @@ public function validate($value, Constraint $constraint) throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\DateTime'); } - if (null === $value || '' === $value || $value instanceof \DateTime) { + if (null === $value || '' === $value || $value instanceof \DateTimeInterface) { return; } diff --git a/src/Symfony/Component/Validator/Constraints/DateValidator.php b/src/Symfony/Component/Validator/Constraints/DateValidator.php index bfeb5fca30ef3..ed836de9aced2 100644 --- a/src/Symfony/Component/Validator/Constraints/DateValidator.php +++ b/src/Symfony/Component/Validator/Constraints/DateValidator.php @@ -47,7 +47,7 @@ public function validate($value, Constraint $constraint) throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Date'); } - if (null === $value || '' === $value || $value instanceof \DateTime) { + if (null === $value || '' === $value || $value instanceof \DateTimeInterface) { return; } diff --git a/src/Symfony/Component/Validator/Constraints/RangeValidator.php b/src/Symfony/Component/Validator/Constraints/RangeValidator.php index 067dd8c1f3db1..ac140dbbf34e2 100644 --- a/src/Symfony/Component/Validator/Constraints/RangeValidator.php +++ b/src/Symfony/Component/Validator/Constraints/RangeValidator.php @@ -33,7 +33,7 @@ public function validate($value, Constraint $constraint) return; } - if (!is_numeric($value) && !$value instanceof \DateTime && !$value instanceof \DateTimeInterface) { + if (!is_numeric($value) && !$value instanceof \DateTimeInterface) { $this->context->buildViolation($constraint->invalidMessage) ->setParameter('{{ value }}', $this->formatValue($value, self::PRETTY_DATE)) ->setCode(Range::INVALID_CHARACTERS_ERROR) @@ -49,7 +49,7 @@ public function validate($value, Constraint $constraint) // This allows to compare with any date/time value supported by // the DateTime constructor: // http://php.net/manual/en/datetime.formats.php - if ($value instanceof \DateTime || $value instanceof \DateTimeInterface) { + if ($value instanceof \DateTimeInterface) { if (is_string($min)) { $min = new \DateTime($min); } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/DateTimeValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/DateTimeValidatorTest.php index ebb1f3e6b74fb..dbc4d1aa688d3 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/DateTimeValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/DateTimeValidatorTest.php @@ -42,6 +42,13 @@ public function testDateTimeClassIsValid() $this->assertNoViolation(); } + public function testDateTimeImmutableClassIsValid() + { + $this->validator->validate(new \DateTimeImmutable(), new DateTime()); + + $this->assertNoViolation(); + } + /** * @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException */ diff --git a/src/Symfony/Component/Validator/Tests/Constraints/DateValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/DateValidatorTest.php index 67bb2340c3630..ec62de6281c58 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/DateValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/DateValidatorTest.php @@ -42,6 +42,13 @@ public function testDateTimeClassIsValid() $this->assertNoViolation(); } + public function testDateTimeImmutableClassIsValid() + { + $this->validator->validate(new \DateTimeImmutable(), new Date()); + + $this->assertNoViolation(); + } + /** * @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException */ diff --git a/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php b/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php index f8b9c1077790a..58fd202c0ea16 100644 --- a/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php @@ -122,6 +122,8 @@ public function dump(Data $data, $output = null) $this->dumpLine(-1); } catch (\Exception $exception) { // Re-thrown below + } catch (\Throwable $exception) { + // Re-thrown below } if ($output) { $this->setOutput($prevOutput); diff --git a/src/Symfony/Component/Yaml/Dumper.php b/src/Symfony/Component/Yaml/Dumper.php index 21351a5c34fc9..9bd9389c07483 100644 --- a/src/Symfony/Component/Yaml/Dumper.php +++ b/src/Symfony/Component/Yaml/Dumper.php @@ -58,7 +58,7 @@ public function dump($input, $inline = 0, $indent = 0, $exceptionOnInvalidType = if ($inline <= 0 || !is_array($input) || empty($input)) { $output .= $prefix.Inline::dump($input, $exceptionOnInvalidType, $objectSupport); } else { - $isAHash = array_keys($input) !== range(0, count($input) - 1); + $isAHash = Inline::isHash($input); foreach ($input as $key => $value) { $willBeInlined = $inline - 1 <= 0 || !is_array($value) || empty($value); diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index 10fbd898f0257..fde2c3276307b 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -157,6 +157,28 @@ public static function dump($value, $exceptionOnInvalidType = false, $objectSupp } } + /** + * Check if given array is hash or just normal indexed array. + * + * @internal + * + * @param array $value The PHP array to check + * + * @return bool true if value is hash array, false otherwise + */ + public static function isHash(array $value) + { + $expectedKey = 0; + + foreach ($value as $key => $val) { + if ($key !== $expectedKey++) { + return true; + } + } + + return false; + } + /** * Dumps a PHP array to a YAML string. * @@ -169,11 +191,7 @@ public static function dump($value, $exceptionOnInvalidType = false, $objectSupp private static function dumpArray($value, $exceptionOnInvalidType, $objectSupport) { // array - $keys = array_keys($value); - $keysCount = count($keys); - if ((1 === $keysCount && '0' == $keys[0]) - || ($keysCount > 1 && array_reduce($keys, function ($v, $w) { return (int) $v + $w; }, 0) === $keysCount * ($keysCount - 1) / 2) - ) { + if ($value && !self::isHash($value)) { $output = array(); foreach ($value as $val) { $output[] = self::dump($val, $exceptionOnInvalidType, $objectSupport); @@ -182,7 +200,7 @@ private static function dumpArray($value, $exceptionOnInvalidType, $objectSuppor return sprintf('[%s]', implode(', ', $output)); } - // mapping + // hash $output = array(); foreach ($value as $key => $val) { $output[] = sprintf('%s: %s', self::dump($key, $exceptionOnInvalidType, $objectSupport), self::dump($val, $exceptionOnInvalidType, $objectSupport)); diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 28b58c36c1e33..087e061ee61e1 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -23,6 +23,7 @@ class Parser const BLOCK_SCALAR_HEADER_PATTERN = '(?P\||>)(?P\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P +#.*)?'; private $offset = 0; + private $totalNumberOfLines; private $lines = array(); private $currentLineNb = -1; private $currentLine = ''; @@ -31,11 +32,13 @@ class Parser /** * Constructor. * - * @param int $offset The offset of YAML document (used for line numbers in error messages) + * @param int $offset The offset of YAML document (used for line numbers in error messages) + * @param int|null $totalNumberOfLines The overall number of lines being parsed */ - public function __construct($offset = 0) + public function __construct($offset = 0, $totalNumberOfLines = null) { $this->offset = $offset; + $this->totalNumberOfLines = $totalNumberOfLines; } /** @@ -60,6 +63,10 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport = $value = $this->cleanup($value); $this->lines = explode("\n", $value); + if (null === $this->totalNumberOfLines) { + $this->totalNumberOfLines = count($this->lines); + } + if (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) { $mbEncoding = mb_internal_encoding(); mb_internal_encoding('UTF-8'); @@ -81,7 +88,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport = $isRef = $mergeNode = false; if (preg_match('#^\-((?P\s+)(?P.+?))?\s*$#u', $this->currentLine, $values)) { if ($context && 'mapping' == $context) { - throw new ParseException('You cannot define a sequence item when in a mapping'); + throw new ParseException('You cannot define a sequence item when in a mapping', $this->getRealCurrentLineNb() + 1, $this->currentLine); } $context = 'sequence'; @@ -93,7 +100,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport = // array if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) { $c = $this->getRealCurrentLineNb() + 1; - $parser = new self($c); + $parser = new self($c, $this->totalNumberOfLines); $parser->refs = &$this->refs; $data[] = $parser->parse($this->getNextEmbedBlock(null, true), $exceptionOnInvalidType, $objectSupport, $objectForMap); } else { @@ -102,7 +109,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport = ) { // this is a compact notation element, add to next block and parse $c = $this->getRealCurrentLineNb(); - $parser = new self($c); + $parser = new self($c, $this->totalNumberOfLines); $parser->refs = &$this->refs; $block = $values['value']; @@ -120,7 +127,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport = } } elseif (preg_match('#^(?P'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P.+?))?\s*$#u', $this->currentLine, $values) && (false === strpos($values['key'], ' #') || in_array($values['key'][0], array('"', "'")))) { if ($context && 'sequence' == $context) { - throw new ParseException('You cannot define a mapping item when in a sequence'); + throw new ParseException('You cannot define a mapping item when in a sequence', $this->currentLineNb + 1, $this->currentLine); } $context = 'mapping'; @@ -167,7 +174,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport = $value = $this->getNextEmbedBlock(); } $c = $this->getRealCurrentLineNb() + 1; - $parser = new self($c); + $parser = new self($c, $this->totalNumberOfLines); $parser->refs = &$this->refs; $parsed = $parser->parse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap); @@ -218,7 +225,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport = } } else { $c = $this->getRealCurrentLineNb() + 1; - $parser = new self($c); + $parser = new self($c, $this->totalNumberOfLines); $parser->refs = &$this->refs; $value = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport, $objectForMap); // Spec: Keys MUST be unique; first one wins. @@ -241,7 +248,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport = } else { // multiple documents are not supported if ('---' === $this->currentLine) { - throw new ParseException('Multiple documents are not supported.'); + throw new ParseException('Multiple documents are not supported.', $this->currentLineNb + 1, $this->currentLine); } // 1-liner optionally followed by newline(s) @@ -488,7 +495,7 @@ private function parseValue($value, $exceptionOnInvalidType, $objectSupport, $ob } if (!array_key_exists($value, $this->refs)) { - throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLine); + throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine); } return $this->refs[$value]; @@ -580,6 +587,8 @@ private function parseBlockScalar($style, $chomping = '', $indentation = 0) if ($notEOF) { $blockLines[] = ''; $this->moveToPreviousLine(); + } elseif (!$notEOF && !$this->isCurrentLineLastLineInDocument()) { + $blockLines[] = ''; } // folded style @@ -686,6 +695,11 @@ private function isCurrentLineComment() return '' !== $ltrimmedLine && $ltrimmedLine[0] === '#'; } + private function isCurrentLineLastLineInDocument() + { + return ($this->offset + $this->currentLineNb) >= ($this->totalNumberOfLines - 1); + } + /** * Cleanups a YAML string to be parsed. * @@ -763,7 +777,7 @@ private function isNextLineUnIndentedCollection() */ private function isStringUnIndentedCollectionItem() { - return 0 === strpos($this->currentLine, '- '); + return '-' === rtrim($this->currentLine) || 0 === strpos($this->currentLine, '- '); } /** diff --git a/src/Symfony/Component/Yaml/Tests/InlineTest.php b/src/Symfony/Component/Yaml/Tests/InlineTest.php index 3da5dd532cd0f..d7ff7ac6cf1b2 100644 --- a/src/Symfony/Component/Yaml/Tests/InlineTest.php +++ b/src/Symfony/Component/Yaml/Tests/InlineTest.php @@ -218,6 +218,24 @@ public function getScalarIndicators() return array(array('|'), array('>')); } + /** + * @dataProvider getDataForIsHash + */ + public function testIsHash($array, $expected) + { + $this->assertSame($expected, Inline::isHash($array)); + } + + public function getDataForIsHash() + { + return array( + array(array(), false), + array(array(1, 2, 3), false), + array(array(2 => 1, 1 => 2, 0 => 3), true), + array(array('foo' => 1, 'bar' => 2), true), + ); + } + public function getTestsForParse() { return array( @@ -424,6 +442,8 @@ public function getTestsForDump() array('[foo, { bar: foo, foo: [foo, { bar: foo }] }, [foo, { bar: foo }]]', array('foo', array('bar' => 'foo', 'foo' => array('foo', array('bar' => 'foo'))), array('foo', array('bar' => 'foo')))), array('[foo, \'@foo.baz\', { \'%foo%\': \'foo is %foo%\', bar: \'%foo%\' }, true, \'@service_container\']', array('foo', '@foo.baz', array('%foo%' => 'foo is %foo%', 'bar' => '%foo%'), true, '@service_container')), + + array('{ foo: { bar: { 1: 2, baz: 3 } } }', array('foo' => array('bar' => array(1 => 2, 'baz' => 3)))), ); } } diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index fc15c32cd8a5d..5b97808c7762f 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -596,7 +596,7 @@ public function testShortcutKeyUnindentedCollectionException() /** * @expectedException \Symfony\Component\Yaml\Exception\ParseException - * @expectedExceptionMessage Multiple documents are not supported. + * @expectedExceptionMessageRegExp /^Multiple documents are not supported.+/ */ public function testMultipleDocumentsNotSupportedException() { @@ -628,6 +628,34 @@ public function testSequenceInAMapping() ); } + public function testSequenceInMappingStartedBySingleDashLine() + { + $yaml = << array( + array( + 'b' => array( + array( + 'bar' => 'baz', + ), + ), + ), + 'foo', + ), + 'd' => 'e', + ); + + $this->assertSame($expected, $this->parser->parse($yaml)); + } + /** * @expectedException \Symfony\Component\Yaml\Exception\ParseException */ @@ -977,6 +1005,7 @@ public function getCommentLikeStringInScalarBlockData() foo # bar baz + EOT , ), @@ -1005,7 +1034,7 @@ public function getCommentLikeStringInScalarBlockData() $expected = array( 'foo' => array( 'bar' => array( - 'scalar-block' => 'line1 line2>', + 'scalar-block' => "line1 line2>\n", ), 'baz' => array( 'foobar' => null,