diff --git a/.composer/composer.json b/.composer/composer.json index 655ee13805f47..d69e9f7626d55 100644 --- a/.composer/composer.json +++ b/.composer/composer.json @@ -1,6 +1,5 @@ { "require": { - "php": ">=5.3.7", - "hirak/prestissimo": "^0.1.15" + "hirak/prestissimo": "^0.1.18" } } diff --git a/.composer/composer.lock b/.composer/composer.lock index 8e037ebb4af81..7027e21d9ac4b 100644 --- a/.composer/composer.lock +++ b/.composer/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "aa7aa2d143fd85800595242996021ada", - "content-hash": "51e9161b78dda1fe149a9e9c106be90b", + "hash": "e02685ff7d2409d34fa87e306fc86ec5", + "content-hash": "2854c05c76a78113c693dbbdd3f9c518", "packages": [ { "name": "hirak/prestissimo", @@ -65,8 +65,6 @@ "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, - "platform": { - "php": ">=5.3.7" - }, + "platform": [], "platform-dev": [] } diff --git a/.travis.yml b/.travis.yml index d82550017d9ab..4718a1090ac7b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,44 +35,46 @@ cache: services: mongodb before_install: + - PHP=$TRAVIS_PHP_VERSION # Matrix lines for intermediate PHP versions are skipped for pull requests - - if [[ ! $deps && ! $TRAVIS_PHP_VERSION = ${MIN_PHP%.*} && $TRAVIS_PHP_VERSION != hhvm && $TRAVIS_PULL_REQUEST != false ]]; then deps=skip; fi; + - if [[ ! $deps && ! $PHP = ${MIN_PHP%.*} && $PHP != hhvm && $TRAVIS_PULL_REQUEST != false ]]; then deps=skip; skip=1; fi # A sigchild-enabled-PHP is used to test the Process component on the lowest PHP matrix line - - if [[ ! $deps && $TRAVIS_PHP_VERSION = ${MIN_PHP%.*} && ! -d php-$MIN_PHP/sapi ]]; then wget http://museum.php.net/php5/php-$MIN_PHP.tar.bz2 -O - | tar -xj; (cd php-$MIN_PHP; ./configure --enable-sigchild --enable-pcntl; make -j2); fi; - - if [[ $TRAVIS_PHP_VERSION != hhvm ]]; then INI_FILE=~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; else INI_FILE=/etc/hhvm/php.ini; fi; - - echo memory_limit = -1 >> $INI_FILE - - echo session.gc_probability = 0 >> $INI_FILE - - if [[ $TRAVIS_PHP_VERSION = 5.* ]]; then echo extension = mongo.so >> $INI_FILE; fi; - - if [[ $TRAVIS_PHP_VERSION = 5.* ]]; then echo extension = memcache.so >> $INI_FILE; fi; - - if [[ $TRAVIS_PHP_VERSION = 5.* ]]; then (echo yes | pecl install -f apcu-4.0.10 && echo apc.enable_cli = 1 >> $INI_FILE); fi; - - if [[ $TRAVIS_PHP_VERSION = 7.* ]]; then (echo yes | pecl install -f apcu-5.1.2 && echo apc.enable_cli = 1 >> $INI_FILE); fi; - - if [[ $TRAVIS_PHP_VERSION = 5.* && ! $deps ]]; then (cd src/Symfony/Component/Debug/Resources/ext && phpize && ./configure && make && echo extension = $(pwd)/modules/symfony_debug.so >> $INI_FILE); fi; - - if [[ $TRAVIS_PHP_VERSION = 5.* ]]; then pecl install -f memcached-2.1.0; fi; - - if [[ $TRAVIS_PHP_VERSION != hhvm ]]; then echo extension = ldap.so >> $INI_FILE; fi; - - if [[ $TRAVIS_PHP_VERSION != hhvm ]]; then phpenv config-rm xdebug.ini; fi; - - if [[ $deps != skip ]]; then composer self-update; fi; - - if [[ $deps != skip && $TRAVIS_REPO_SLUG = symfony/symfony ]]; then cp .composer/* ~/.composer/; composer global install; fi; - - if [[ $deps != skip ]]; then ./phpunit install; fi; - - export PHPUNIT=$(readlink -f ./phpunit) + - if [[ ! $deps && $PHP = ${MIN_PHP%.*} && ! -d php-$MIN_PHP/sapi ]]; then wget http://museum.php.net/php5/php-$MIN_PHP.tar.bz2 -O - | tar -xj; (cd php-$MIN_PHP; ./configure --enable-sigchild --enable-pcntl; make -j2); fi + - if [[ $PHP != hhvm ]]; then INI_FILE=~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; else INI_FILE=/etc/hhvm/php.ini; fi + - if [[ ! $skip ]]; then echo memory_limit = -1 >> $INI_FILE; fi + - if [[ ! $skip ]]; then echo session.gc_probability = 0 >> $INI_FILE; fi + - if [[ ! $skip && $PHP = 5.* ]]; then echo extension = mongo.so >> $INI_FILE; fi + - if [[ ! $skip && $PHP = 5.* ]]; then echo extension = memcache.so >> $INI_FILE; fi + - if [[ ! $skip && $PHP = 5.* ]]; then (echo yes | pecl install -f apcu-4.0.10 && echo apc.enable_cli = 1 >> $INI_FILE); fi + - if [[ ! $skip && $PHP = 7.* ]]; then (echo yes | pecl install -f apcu-5.1.2 && echo apc.enable_cli = 1 >> $INI_FILE); fi + - if [[ ! $deps && $PHP = 5.* ]]; then (cd src/Symfony/Component/Debug/Resources/ext && phpize && ./configure && make && echo extension = $(pwd)/modules/symfony_debug.so >> $INI_FILE); fi + - if [[ ! $skip && $PHP = 5.* ]]; then pecl install -f memcached-2.1.0; fi + - if [[ ! $skip && $PHP != hhvm ]]; then echo extension = ldap.so >> $INI_FILE; fi + - if [[ ! $skip && $PHP != hhvm ]]; then phpenv config-rm xdebug.ini; fi + - if [[ ! $skip ]]; then composer self-update --stable; fi + - if [[ ! $skip ]]; then cp .composer/* ~/.composer/; composer global install; fi + - if [[ ! $skip ]]; then ./phpunit install; fi + - if [[ ! $skip && $deps ]]; then composer global remove hirak/prestissimo; fi + - if [[ ! $skip ]]; then export PHPUNIT=$(readlink -f ./phpunit); fi install: - - if [[ $deps != skip ]]; then COMPONENTS=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -printf '%h\n'); fi; + - if [[ ! $skip ]]; then COMPONENTS=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -printf '%h\n'); fi # Create local composer packages for each patched components and reference them in composer.json files when cross-testing components - - if [[ $deps != skip && $deps ]]; then php .travis.php $TRAVIS_COMMIT_RANGE $TRAVIS_BRANCH $COMPONENTS; fi; + - if [[ ! $skip && $deps ]]; then php .travis.php $TRAVIS_COMMIT_RANGE $TRAVIS_BRANCH $COMPONENTS; fi # For the master branch when deps=high, the version before master is checked out and tested with the locally patched components - - if [[ $deps = high && $TRAVIS_BRANCH = master ]]; then SYMFONY_VERSION=$(git ls-remote --heads | grep -o '/[1-9].*' | tail -n 1 | sed s/.//); else SYMFONY_VERSION=$(cat composer.json | grep '^ *"dev-master". *"[1-9]' | grep -o '[0-9.]*'); fi; - - if [[ $deps = high && $TRAVIS_BRANCH = master ]]; then git fetch origin $SYMFONY_VERSION; git checkout -m FETCH_HEAD; COMPONENTS=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -printf '%h\n'); ./phpunit install; fi; + - if [[ $deps = high && $TRAVIS_BRANCH = master ]]; then SYMFONY_VERSION=$(git ls-remote --heads | grep -o '/[1-9].*' | tail -n 1 | sed s/.//); else SYMFONY_VERSION=$(cat composer.json | grep '^ *"dev-master". *"[1-9]' | grep -o '[0-9.]*'); fi + - if [[ $deps = high && $TRAVIS_BRANCH = master ]]; then git fetch origin $SYMFONY_VERSION; git checkout -m FETCH_HEAD; COMPONENTS=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -printf '%h\n'); ./phpunit install; fi # Legacy tests are skipped when deps=high and when the current branch version has not the same major version number than the next one - - if [[ $deps = high && ${SYMFONY_VERSION%.*} != $(git show $(git ls-remote --heads | grep -FA1 /$SYMFONY_VERSION | tail -n 1):composer.json | grep '^ *"dev-master". *"[1-9]' | grep -o '[0-9]*' | head -n 1) ]]; then LEGACY=,legacy; fi; - - export COMPOSER_ROOT_VERSION=$SYMFONY_VERSION.x-dev; - - if [[ ! $deps ]]; then composer update; else export SYMFONY_DEPRECATIONS_HELPER=weak; fi; - - if [[ $TRAVIS_BRANCH = master ]]; then export SYMFONY_PHPUNIT_OVERLOAD=1; fi; - - if [[ $TRAVIS_PHP_VERSION != hhvm ]]; then php -i; else hhvm --php -r 'print_r($_SERVER);print_r(ini_get_all());'; fi; + - if [[ $deps = high && ${SYMFONY_VERSION%.*} != $(git show $(git ls-remote --heads | grep -FA1 /$SYMFONY_VERSION | tail -n 1):composer.json | grep '^ *"dev-master". *"[1-9]' | grep -o '[0-9]*' | head -n 1) ]]; then LEGACY=,legacy; fi + - export COMPOSER_ROOT_VERSION=$SYMFONY_VERSION.x-dev + - if [[ ! $deps ]]; then composer update; else export SYMFONY_DEPRECATIONS_HELPER=weak; fi + - if [[ $TRAVIS_BRANCH = master ]]; then export SYMFONY_PHPUNIT_OVERLOAD=1; fi + - if [[ $PHP != hhvm ]]; then php -i; else hhvm --php -r 'print_r($_SERVER);print_r(ini_get_all());'; fi script: - - if [[ ! $deps ]]; then echo "$COMPONENTS" | parallel --gnu '$PHPUNIT --exclude-group tty,benchmark,intl-data {}'; fi; - - if [[ ! $deps ]]; then echo -e "\\nRunning tests requiring tty"; $PHPUNIT --group tty; fi; - - if [[ ! $deps && $TRAVIS_PHP_VERSION = ${MIN_PHP%.*} ]]; then echo -e "1\\n0" | xargs -I{} sh -c 'echo "\\nPHP --enable-sigchild enhanced={}" && ENHANCE_SIGCHLD={} php-$MIN_PHP/sapi/cli/php .phpunit/phpunit-4.8/phpunit --colors=always src/Symfony/Component/Process/'; fi; - - if [[ $deps = high ]]; then echo "$COMPONENTS" | parallel --gnu -j10% 'cd {}; composer update; $PHPUNIT --exclude-group tty,benchmark,intl-data'$LEGACY; fi; - - if [[ $deps = low ]]; then echo "$COMPONENTS" | parallel --gnu -j10% 'cd {}; composer update --prefer-lowest --prefer-stable; $PHPUNIT --exclude-group tty,benchmark,intl-data'; fi; - - if [[ $deps = skip ]]; then echo This matrix line is skipped for pull requests.; fi; + - if [[ $skip ]]; then echo -e "\\n\\e[1;34mIntermediate PHP version $PHP is skipped for pull requests.\\e[0m"; fi + - if [[ ! $deps ]]; then echo "$COMPONENTS" | parallel --gnu '$PHPUNIT --exclude-group tty,benchmark,intl-data {}'; fi + - if [[ ! $deps ]]; then echo -e "\\nRunning tests requiring tty"; $PHPUNIT --group tty; fi + - if [[ ! $deps && $PHP = ${MIN_PHP%.*} ]]; then echo -e "1\\n0" | xargs -I{} sh -c 'echo "\\nPHP --enable-sigchild enhanced={}" && ENHANCE_SIGCHLD={} php-$MIN_PHP/sapi/cli/php .phpunit/phpunit-4.8/phpunit --colors=always src/Symfony/Component/Process/'; fi + - if [[ $deps = high ]]; then echo "$COMPONENTS" | parallel --gnu -j10% 'cd {}; composer update --no-progress --ansi; $PHPUNIT --exclude-group tty,benchmark,intl-data'$LEGACY; fi + - if [[ $deps = low ]]; then echo "$COMPONENTS" | parallel --gnu -j10% 'cd {}; composer update --no-progress --ansi --prefer-lowest --prefer-stable; $PHPUNIT --exclude-group tty,benchmark,intl-data'; fi diff --git a/CHANGELOG-2.8.md b/CHANGELOG-2.8.md index 38abdc97b27f1..854716d996a75 100644 --- a/CHANGELOG-2.8.md +++ b/CHANGELOG-2.8.md @@ -7,6 +7,102 @@ in 2.8 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v2.8.0...v2.8.1 +* 2.8.5 (2016-04-29) + + * bug #18180 [Form] fixed BC break with pre selection of choices with `ChoiceType` and its children (HeahDude) + * bug #18645 [Console] Fix wrong exceptions being thrown (JhonnyL) + * bug #18562 [WebProfilerBunde] Give an absolute url in case the request occured from another domain (romainneutron) + * bug #18600 [DI] Fix AutowirePass fatal error with classes that have non-existing parents (hason, nicolas-grekas) + * bug #18603 [PropertyAccess] ->getValue() should be read-only (nicolas-grekas) + * bug #18593 [VarDumper] Fix dumping type hints for non-existing parent classes (nicolas-grekas) + * bug #18596 [DI] Fix internal caching in AutowirePass (nicolas-grekas) + * bug #18581 [Console] [TableHelper] make it work with SymfonyStyle. (aitboudad) + * bug #18280 [Routing] add query param if value is different from default (Tobion) + * bug #18540 Replace iconv_*() uses by mb_*(), add mbstring polyfill when required (nicolas-grekas) + * bug #18496 [Console] use ANSI escape sequences in ProgressBar overwrite method (alekitto) + * bug #18490 [LDAP] Free the search result after a search to free memory (hiddewie) + * bug #18491 [DependencyInjection] anonymous services are always private (xabbuh) + * bug #18515 [Filesystem] Better error handling in remove() (nicolas-grekas) + * bug #18360 [PropertyInfo] Extract nullable and collection key type for Doctrine associations (teohhanhui) + * bug #18449 [PropertyAccess] Fix regression (nicolas-grekas) + * bug #18429 [Console] Correct time formatting. (camporter) + * bug #18457 [WebProfilerBundle] Fixed error from unset twig variable (simonsargeant) + * bug #18467 [DependencyInjection] Resolve aliases before removing abstract services + add tests (nicolas-grekas) + * bug #18469 Force profiler toolbar svg display (pyrech) + * bug #18460 [DomCrawler] Fix select option with empty value (Matt Wells) + * bug #18425 [Security] Fixed SwitchUserListener when exiting an impersonation with AnonymousToken (lyrixx) + * bug #18317 [Form] fix "prototype" not required when parent form is not required (HeahDude) + * bug #18439 [Logging] Add support for Firefox (43+) in ChromePhpHandler (arjenm) + * bug #18385 Detect CLI color support for Windows 10 build 10586 (mlocati) + * bug #18426 [EventDispatcher] Try first if the event is Stopped (lyrixx) + * bug #18407 Fixed the "hover" state of the profiler sidebar menu (javiereguiluz) + * bug #18394 [FrameworkBundle] Return the invokable service if its name is the class name (dunglas) + * bug #18347 Fixed the styles of the Symfony icon in the web debug toolbar (javiereguiluz) + * bug #18265 Optimize ReplaceAliasByActualDefinitionPass (ajb-in) + * bug #18349 [Process] Fix stream_select priority when writing to stdin (nicolas-grekas) + * bug #18358 [Form] NumberToLocalizedStringTransformer should return floats when possible (nicolas-grekas) + * bug #17926 [DependencyInjection] Enable alias for service_container (hason) + * bug #18352 [Debug] Fix case sensitivity checks (nicolas-grekas) + * bug #18336 [Debug] Fix handling of php7 throwables (nicolas-grekas) + * bug #18354 [FrameworkBundle][TwigBridge] fix high deps tests (xabbuh) + * bug #18312 [ClassLoader] Fix storing not-found classes in APC cache (nicolas-grekas) + +* 2.8.4 (2016-03-27) + + * bug #18298 [Validator] do not treat payload as callback (xabbuh) + * bug #18275 [Form] Fix BC break introduced in #14403 (HeahDude) + * bug #18271 [FileSystem] Google app engine filesystem (swordbeta) + * bug #18255 [HttpFoundation] Fix support of custom mime types with parameters (Ener-Getick) + * bug #18272 [Bridge\PhpUnit] Workaround old phpunit bug, no colors in weak mode, add tests (nicolas-grekas) + * bug #18259 [PropertyAccess] Backport fixes from 2.7 (nicolas-grekas) + * bug #18261 [PropertyAccess] Fix isPropertyWritable not using the reflection cache (nicolas-grekas) + * bug #18224 [PropertyAccess] Remove most ref mismatches to improve perf (nicolas-grekas) + * bug #18237 [WebProfilerBundle] Added table-layout property to AJAX toolbar css (kevintweber) + * bug #18209 [PropertyInfo] Support Doctrine custom mapping type in DoctrineExtractor (teohhanhui) + * bug #18210 [PropertyAccess] Throw an UnexpectedTypeException when the type do not match (dunglas, nicolas-grekas) + * bug #18216 [Intl] Fix invalid numeric literal on PHP 7 (nicolas-grekas) + * bug #18147 [Validator] EmailValidator cannot extract hostname if email contains multiple @ symbols (natechicago) + * bug #18023 [Process] getIncrementalOutput should work without calling getOutput (romainneutron) + * bug #18175 [Translation] Add support for fuzzy tags in PoFileLoader (nud) + * bug #18179 [Form] Fix NumberToLocalizedStringTransformer::reverseTransform with big integers (ovrflo, nicolas-grekas) + * bug #18164 [HttpKernel] set s-maxage only if all responses are cacheable (xabbuh) + * bug #18150 [Process] Wait a bit less on Windows (nicolas-grekas) + * bug #18130 [Debug] Replaced logic for detecting filesystem case sensitivity (Dan Blows) + * bug #18137 Autowiring the concrete class too - consistent with behavior of other services (weaverryan) + * bug #18087 [WebProfiler] Sidebar button padding (rvanlaak) + * bug #18080 [HttpFoundation] Set the Content-Range header if the requested Range is unsatisfied (jakzal) + * bug #18084 [HttpFoundation] Avoid warnings when checking malicious IPs (jakzal) + * bug #18066 [Process] Fix pipes handling (nicolas-grekas) + * bug #18078 [Console] Fix an autocompletion question helper issue with non-sequentially indexed choices (jakzal) + * bug #18048 [HttpKernel] Fix mem usage when stripping the prod container (nicolas-grekas) + * bug #18065 [Finder] Partially revert #17134 to fix a regression (jakzal) + * bug #18018 [HttpFoundation] exception when registering bags for started sessions (xabbuh) + * bug #18054 [Filesystem] Fix false positive in ->remove() (nicolas-grekas) + * bug #18049 [Validator] Fix the locale validator so it treats a locale alias as a valid locale (jakzal) + * bug #18019 [Intl] Update ICU to version 55 (jakzal) + * bug #18015 [Process] Fix memory issue when using large input streams (romainneutron) + * bug #16656 [HttpFoundation] automatically generate safe fallback filename (xabbuh) + * bug #15794 [Console] default to stderr in the console helpers (alcohol) + * bug #17984 Allow to normalize \Traversable when serializing xml (Ener-Getick) + * bug #17434 Improved the error message when a template is not found (rvanginneken, javiereguiluz) + * bug #17687 Improved the error message when using "@" in a decorated service (javiereguiluz) + * bug #17744 Improve error reporting in router panel of web profiler (javiereguiluz) + * bug #17894 [FrameworkBundle] Fix a regression in handling absolute template paths (jakzal) + * bug #17990 [DoctrineBridge][Form] Fix performance regression in EntityType (kimlai) + * bug #17595 [HttpKernel] Remove _path from query parameters when fragment is a subrequest (cmenning) + * bug #17986 [DomCrawler] Dont use LIBXML_PARSEHUGE by default (nicolas-grekas) + * bug #17668 add 'guid' to list of exception to filter out (garak) + * bug #17615 Ensure backend slashes for symlinks on Windows systems (cpsitgmbh) + * bug #17626 Try to delete broken symlinks (IchHabRecht) + * bug #17978 [Yaml] ensure dump indentation to be greather than zero (xabbuh) + * bug #16886 [Form] [ChoiceType] Prefer placeholder to empty_value (boite) + * bug #17976 [WebProfilerBundle] fix debug toolbar rendering by removing inadvertently added links (craue) + * bug #17971 Variadic controller params (NiR-, fabpot) + * bug #17876 [DependencyInjection] Fixing autowiring bug when some args are set (weaverryan) + * bug #17568 Improved Bootstrap form theme for hidden fields (javiereguiluz) + * bug #17561 [WebProfilerBundle] Fix design issue in profiler when having errors in forms (Pierstoval) + * bug #17925 [Bridge] The WebProcessor now forwards the client IP (magnetik) + * 2.8.3 (2016-02-28) * bug #17947 Fix - #17676 (backport #17919 to 2.3) (Ocramius) diff --git a/appveyor.yml b/appveyor.yml index b29e117343008..4a64d1494c765 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,11 +21,11 @@ install: - IF %PHP%==1 appveyor DownloadFile https://curl.haxx.se/ca/cacert.pem - IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/releases/archives/php-5.3.11-nts-Win32-VC9-x86.zip - IF %PHP%==1 7z x php-5.3.11-nts-Win32-VC9-x86.zip -y >nul - - IF %PHP%==1 appveyor DownloadFile http://nebm.ist.utl.pt/~glopes/misc/intl_win/ICU-51.2-dlls.zip + - IF %PHP%==1 appveyor DownloadFile https://raw.githubusercontent.com/symfony/binary-utils/master/ICU-51.2-dlls.zip - IF %PHP%==1 7z x ICU-51.2-dlls.zip -y >nul - IF %PHP%==1 del /Q *.zip - IF %PHP%==1 cd ext - - IF %PHP%==1 appveyor DownloadFile http://nebm.ist.utl.pt/~glopes/misc/intl_win/php_intl-3.0.0-5.3-nts-vc9-x86.zip + - IF %PHP%==1 appveyor DownloadFile https://raw.githubusercontent.com/symfony/binary-utils/master/php_intl-3.0.0-5.3-nts-vc9-x86.zip - IF %PHP%==1 7z x php_intl-3.0.0-5.3-nts-vc9-x86.zip -y >nul - IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/pecl/releases/apcu/4.0.10/php_apcu-4.0.10-5.3-nts-vc9-x86.zip - IF %PHP%==1 7z x php_apcu-4.0.10-5.3-nts-vc9-x86.zip -y >nul @@ -50,12 +50,12 @@ 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 - - appveyor DownloadFile https://getcomposer.org/composer.phar + - IF %PHP%==1 appveyor DownloadFile https://getcomposer.org/download/1.0.2/composer.phar - copy /Y php.ini-max php.ini - cd c:\projects\symfony - mkdir %APPDATA%\Composer - - IF %APPVEYOR_REPO_NAME%==symfony/symfony copy /Y .composer\* %APPDATA%\Composer\ - - IF %APPVEYOR_REPO_NAME%==symfony/symfony composer global install --no-progress --ansi || echo curl.cainfo needs PHP 5.3.7 + - copy /Y .composer\* %APPDATA%\Composer\ + - composer global install --no-progress --ansi - php phpunit install - IF %APPVEYOR_REPO_BRANCH%==master (SET COMPOSER_ROOT_VERSION=dev-master) ELSE (SET COMPOSER_ROOT_VERSION=%APPVEYOR_REPO_BRANCH%.x-dev) - composer update --no-progress --ansi diff --git a/composer.json b/composer.json index 288b9d9b0dedd..ee035f235b3e0 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "doctrine/common": "~2.4", "twig/twig": "~1.23|~2.0", "psr/log": "~1.0", - "symfony/security-acl": "~2.7", + "symfony/security-acl": "~2.7|~3.0.0", "symfony/polyfill-apcu": "~1.1", "symfony/polyfill-intl-icu": "~1.0", "symfony/polyfill-mbstring": "~1.0", @@ -93,7 +93,11 @@ }, "autoload": { "psr-4": { - "Symfony\\Bridge\\": "src/Symfony/Bridge/", + "Symfony\\Bridge\\Doctrine\\": "src/Symfony/Bridge/Doctrine/", + "Symfony\\Bridge\\Monolog\\": "src/Symfony/Bridge/Monolog/", + "Symfony\\Bridge\\ProxyManager\\": "src/Symfony/Bridge/ProxyManager/", + "Symfony\\Bridge\\Swiftmailer\\": "src/Symfony/Bridge/Swiftmailer/", + "Symfony\\Bridge\\Twig\\": "src/Symfony/Bridge/Twig/", "Symfony\\Bundle\\": "src/Symfony/Bundle/", "Symfony\\Component\\": "src/Symfony/Component/" }, @@ -104,6 +108,11 @@ "**/Tests/" ] }, + "autoload-dev": { + "psr-4": { + "Symfony\\Bridge\\PhpUnit\\": "src/Symfony/Bridge/PhpUnit/" + } + }, "minimum-stability": "dev", "extra": { "branch-alias": { diff --git a/phpunit b/phpunit index d2da42616d4eb..6d66f7b2b252a 100755 --- a/phpunit +++ b/phpunit @@ -51,6 +51,7 @@ if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit") || md5_file(__ $zip->extractTo(getcwd()); $zip->close(); chdir("phpunit-$PHPUNIT_VERSION"); + passthru("$COMPOSER remove --no-update phpspec/prophecy"); passthru("$COMPOSER remove --no-update symfony/yaml"); passthru("$COMPOSER require --dev --no-update symfony/phpunit-bridge \">=3.1@dev\""); passthru("$COMPOSER install --prefer-dist --no-progress --ansi", $exit); diff --git a/src/Symfony/Bridge/Doctrine/Logger/DbalLogger.php b/src/Symfony/Bridge/Doctrine/Logger/DbalLogger.php index 18fb341e5b74c..88b48e1ddb47b 100644 --- a/src/Symfony/Bridge/Doctrine/Logger/DbalLogger.php +++ b/src/Symfony/Bridge/Doctrine/Logger/DbalLogger.php @@ -95,8 +95,8 @@ private function normalizeParams(array $params) } // detect if the too long string must be shorten - if (self::MAX_STRING_LENGTH < iconv_strlen($params[$index], 'UTF-8')) { - $params[$index] = iconv_substr($params[$index], 0, self::MAX_STRING_LENGTH - 6, 'UTF-8').' [...]'; + if (self::MAX_STRING_LENGTH < mb_strlen($params[$index], 'UTF-8')) { + $params[$index] = mb_substr($params[$index], 0, self::MAX_STRING_LENGTH - 6, 'UTF-8').' [...]'; continue; } } diff --git a/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php b/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php index ecd25088be0f4..6e8c29e16b0dd 100644 --- a/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php +++ b/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php @@ -71,7 +71,9 @@ public function getTypes($class, $property, array $context = array()) if ($metadata->isSingleValuedAssociation($property)) { if ($metadata instanceof ClassMetadataInfo) { - $nullable = isset($metadata->discriminatorColumn['nullable']) ? $metadata->discriminatorColumn['nullable'] : false; + $associationMapping = $metadata->getAssociationMapping($property); + + $nullable = $this->isAssociationNullable($associationMapping); } else { $nullable = false; } @@ -79,12 +81,25 @@ public function getTypes($class, $property, array $context = array()) return array(new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $class)); } + $collectionKeyType = Type::BUILTIN_TYPE_INT; + + if ($metadata instanceof ClassMetadataInfo) { + $associationMapping = $metadata->getAssociationMapping($property); + + if (isset($associationMapping['indexBy'])) { + $indexProperty = $associationMapping['indexBy']; + $typeOfField = $metadata->getTypeOfField($indexProperty); + + $collectionKeyType = $this->getPhpType($typeOfField); + } + } + return array(new Type( Type::BUILTIN_TYPE_OBJECT, false, 'Doctrine\Common\Collections\Collection', true, - new Type(Type::BUILTIN_TYPE_INT), + new Type($collectionKeyType), new Type(Type::BUILTIN_TYPE_OBJECT, false, $class) )); } @@ -118,6 +133,35 @@ public function getTypes($class, $property, array $context = array()) } } + /** + * Determines whether an association is nullable. + * + * @param array $associationMapping + * + * @return bool + * + * @see https://github.com/doctrine/doctrine2/blob/v2.5.4/lib/Doctrine/ORM/Tools/EntityGenerator.php#L1221-L1246 + */ + private function isAssociationNullable(array $associationMapping) + { + if (isset($associationMapping['id']) && $associationMapping['id']) { + return false; + } + + if (!isset($associationMapping['joinColumns'])) { + return true; + } + + $joinColumns = $associationMapping['joinColumns']; + foreach ($joinColumns as $joinColumn) { + if (isset($joinColumn['nullable']) && !$joinColumn['nullable']) { + return false; + } + } + + return true; + } + /** * Gets the corresponding built-in PHP type. * diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php index 0f056953063c4..469d8fa3d1d5d 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php @@ -287,7 +287,7 @@ public function testSubmitSingleExpandedNull() $field->submit(null); $this->assertNull($field->getData()); - $this->assertNull($field->getViewData()); + $this->assertSame('', $field->getViewData(), 'View data is always a string'); } public function testSubmitSingleNonExpandedNull() diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php index ca098b6fc8e70..1bc57b1140fae 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php @@ -54,6 +54,7 @@ public function testGetProperties() 'customFoo', 'foo', 'bar', + 'indexedBar', ), $this->extractor->getProperties('Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineDummy') ); @@ -75,7 +76,7 @@ public function typesProvider() array('bool', array(new Type(Type::BUILTIN_TYPE_BOOL))), array('binary', array(new Type(Type::BUILTIN_TYPE_RESOURCE))), array('json', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true))), - array('foo', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineRelation'))), + array('foo', array(new Type(Type::BUILTIN_TYPE_OBJECT, true, 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineRelation'))), array('bar', array(new Type( Type::BUILTIN_TYPE_OBJECT, false, @@ -84,6 +85,14 @@ public function typesProvider() new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineRelation') ))), + array('indexedBar', array(new Type( + Type::BUILTIN_TYPE_OBJECT, + false, + 'Doctrine\Common\Collections\Collection', + true, + new Type(Type::BUILTIN_TYPE_STRING), + new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineRelation') + ))), array('simpleArray', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING)))), array('customFoo', null), array('notMapped', null), diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php index 0b6b7bb544039..317d14257b203 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php @@ -40,6 +40,11 @@ class DoctrineDummy */ public $bar; + /** + * @ManyToMany(targetEntity="DoctrineRelation", indexBy="guid") + */ + protected $indexedBar; + /** * @Column(type="guid") */ diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php index bfb27e9338d99..23f64d829e030 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php @@ -12,6 +12,7 @@ namespace Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures; use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; /** @@ -26,4 +27,9 @@ class DoctrineRelation * @Column(type="smallint") */ public $id; + + /** + * @Column(type="guid") + */ + protected $guid; } diff --git a/src/Symfony/Bridge/Doctrine/composer.json b/src/Symfony/Bridge/Doctrine/composer.json index f07527f5cb523..f0f79f522bbd1 100644 --- a/src/Symfony/Bridge/Doctrine/composer.json +++ b/src/Symfony/Bridge/Doctrine/composer.json @@ -17,12 +17,13 @@ ], "require": { "php": ">=5.3.9", - "doctrine/common": "~2.4" + "doctrine/common": "~2.4", + "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { "symfony/stopwatch": "~2.2|~3.0.0", "symfony/dependency-injection": "~2.2|~3.0.0", - "symfony/form": "~2.8|~3.0.0", + "symfony/form": "~2.8.5|~3.0.5", "symfony/http-kernel": "~2.2|~3.0.0", "symfony/property-access": "~2.3|~3.0.0", "symfony/property-info": "~2.8|3.0", diff --git a/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php b/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php index 267b012966901..2636bc3b8980e 100644 --- a/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php +++ b/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php @@ -41,7 +41,7 @@ public function onKernelResponse(FilterResponseEvent $event) return; } - if (!preg_match('{\bChrome/\d+[\.\d+]*\b}', $event->getRequest()->headers->get('User-Agent'))) { + if (!preg_match('{\b(?:Chrome/\d+(?:\.\d+)*|Firefox/(?:4[3-9]|[5-9]\d|\d{3,})(?:\.\d)*)\b}', $event->getRequest()->headers->get('User-Agent'))) { $this->sendHeaders = false; $this->headers = array(); diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php index efd17102923a7..330798fa6eb89 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php @@ -194,7 +194,11 @@ public static function register($mode = 0) private static function hasColorSupport() { if ('\\' === DIRECTORY_SEPARATOR) { - return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); + return + 0 >= version_compare('10.0.10586', PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD) + || false !== getenv('ANSICON') + || 'ON' === getenv('ConEmuANSI') + || 'xterm' === getenv('TERM'); } return defined('STDOUT') && function_exists('posix_isatty') && @posix_isatty(STDOUT); diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig index 7f9684c0de4df..5e1441126ad52 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig @@ -322,7 +322,7 @@ {%- if read_only %} readonly="readonly"{% endif -%} {%- if disabled %} disabled="disabled"{% endif -%} {%- if required %} required="required"{% endif -%} - {%- for attrname, attrvalue in attr if 'readonly' != attrname -%} + {%- for attrname, attrvalue in attr if 'readonly' != attrname or not read_only -%} {{- " " -}} {%- if attrname in ['placeholder', 'title'] -%} {{- attrname }}="{{ translation_domain is same as(false) ? attrvalue : attrvalue|trans({}, translation_domain) }}" diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php index 3c8eca370ffec..f6d9877f1f10b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php @@ -80,7 +80,7 @@ protected function configure() php %command.full_name% --parameters -Display a specific parameter by specifying his name with the --parameter option: +Display a specific parameter by specifying its name with the --parameter option: php %command.full_name% --parameter=kernel.debug @@ -100,25 +100,21 @@ protected function execute(InputInterface $input, OutputInterface $output) } $this->validateInput($input); + $object = $this->getContainerBuilder(); if ($input->getOption('parameters')) { - $object = $this->getContainerBuilder()->getParameterBag(); + $object = $object->getParameterBag(); $options = array(); } elseif ($parameter = $input->getOption('parameter')) { - $object = $this->getContainerBuilder(); $options = array('parameter' => $parameter); } elseif ($input->getOption('tags')) { - $object = $this->getContainerBuilder(); $options = array('group_by' => 'tags', 'show_private' => $input->getOption('show-private')); } elseif ($tag = $input->getOption('tag')) { - $object = $this->getContainerBuilder(); $options = array('tag' => $tag, 'show_private' => $input->getOption('show-private')); } elseif ($name = $input->getArgument('name')) { - $object = $this->getContainerBuilder(); $name = $this->findProperServiceName($input, $io, $object, $name); $options = array('id' => $name); } else { - $object = $this->getContainerBuilder(); $options = array('show_private' => $input->getOption('show-private')); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php index 6804ded30d0e5..31593b9b80506 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php @@ -78,6 +78,10 @@ protected function createController($controller) */ protected function instantiateController($class) { + if ($this->container->has($class)) { + return $this->container->get($class); + } + $controller = parent::instantiateController($class); if ($controller instanceof ContainerAwareInterface) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_attributes.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_attributes.html.php index 8e3e35e8342a3..6e2f74836e6f0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_attributes.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_attributes.html.php @@ -2,7 +2,7 @@ disabled="disabled" required="required" $v): ?> - + escape($k), $view->escape(false !== $translation_domain ? $view['translator']->trans($v, array(), $translation_domain) : $v)) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php index 76a3489fe1551..b4dc8ada555b6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php @@ -87,6 +87,8 @@ public function testGetControllerService() public function testGetControllerInvokableService() { + $invokableController = new InvokableController('bar'); + $container = $this->createMockContainer(); $container->expects($this->once()) ->method('has') @@ -96,7 +98,7 @@ public function testGetControllerInvokableService() $container->expects($this->once()) ->method('get') ->with('foo') - ->will($this->returnValue($this)) + ->will($this->returnValue($invokableController)) ; $resolver = $this->createControllerResolver(null, null, $container); @@ -105,7 +107,33 @@ public function testGetControllerInvokableService() $controller = $resolver->getController($request); - $this->assertInstanceOf(get_class($this), $controller); + $this->assertEquals($invokableController, $controller); + } + + public function testGetControllerInvokableServiceWithClassNameAsName() + { + $invokableController = new InvokableController('bar'); + $className = __NAMESPACE__.'\InvokableController'; + + $container = $this->createMockContainer(); + $container->expects($this->once()) + ->method('has') + ->with($className) + ->will($this->returnValue(true)) + ; + $container->expects($this->once()) + ->method('get') + ->with($className) + ->will($this->returnValue($invokableController)) + ; + + $resolver = $this->createControllerResolver(null, null, $container); + $request = Request::create('/'); + $request->attributes->set('_controller', $className); + + $controller = $resolver->getController($request); + + $this->assertEquals($invokableController, $controller); } /** @@ -178,3 +206,14 @@ public function __invoke() { } } + +class InvokableController +{ + public function __construct($bar) // mandatory argument to prevent automatic instantiation + { + } + + public function __invoke() + { + } +} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php b/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php index 2be420ba8b0b0..d77eb279f4a69 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\WebProfilerBundle\Controller; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Matcher\UrlMatcherInterface; use Symfony\Component\Routing\Matcher\TraceableUrlMatcher; diff --git a/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php b/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php index 71c5090fc8a53..ee537af4f9889 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php +++ b/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php @@ -65,7 +65,7 @@ public function onKernelResponse(FilterResponseEvent $event) try { $response->headers->set( 'X-Debug-Token-Link', - $this->urlGenerator->generate('_profiler', array('token' => $response->headers->get('X-Debug-Token'))) + $this->urlGenerator->generate('_profiler', array('token' => $response->headers->get('X-Debug-Token')), UrlGeneratorInterface::ABSOLUTE_URL) ); } catch (\Exception $e) { $response->headers->set('X-Debug-Error', get_class($e).': '.$e->getMessage()); diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/config.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/config.html.twig index a2a7dff8b49f0..545d34e48697e 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/config.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/config.html.twig @@ -50,24 +50,24 @@ - {% if 'n/a' != collector.appname %} + {% if 'n/a' is not same as(collector.appname) %}
Kernel name {{ collector.appname }}
{% endif %} - {% if 'n/a' != collector.env %} + {% if 'n/a' is not same as(collector.env) %}
Environment {{ collector.env }}
{% endif %} - {% if 'n/a' != collector.debug %} + {% if 'n/a' is not same as(collector.debug) %}
Debug - {{ collector.debug ? 'enabled' : 'disabled' }} + {{ collector.debug ? 'enabled' : 'disabled' }}
{% endif %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/symfony.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/symfony.svg index 170e4c0cf941b..30b1c2e9aad51 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/symfony.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/symfony.svg @@ -1,4 +1,4 @@ - + formatLineCount)); + $this->overwrite(''); } /** @@ -513,37 +512,22 @@ private function setMaxSteps($max) */ private function overwrite($message) { - $lines = explode("\n", $message); - - // append whitespace to match the line's length - if (null !== $this->lastMessagesLength) { - foreach ($lines as $i => $line) { - if ($this->lastMessagesLength > Helper::strlenWithoutDecoration($this->output->getFormatter(), $line)) { - $lines[$i] = str_pad($line, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT); - } - } - } - if ($this->overwrite) { - // move back to the beginning of the progress bar before redrawing it + // Move the cursor to the beginning of the line $this->output->write("\x0D"); - } elseif ($this->step > 0) { - // move to new line - $this->output->writeln(''); - } - if ($this->formatLineCount) { - $this->output->write(sprintf("\033[%dA", $this->formatLineCount)); - } - $this->output->write(implode("\n", $lines)); + // Erase the line + $this->output->write("\x1B[2K"); - $this->lastMessagesLength = 0; - foreach ($lines as $line) { - $len = Helper::strlenWithoutDecoration($this->output->getFormatter(), $line); - if ($len > $this->lastMessagesLength) { - $this->lastMessagesLength = $len; + // Erase previous lines + if ($this->formatLineCount > 0) { + $this->output->write(str_repeat("\x1B[1A\x1B[2K", $this->formatLineCount)); } + } elseif ($this->step > 0) { + $this->output->writeln(''); } + + $this->output->write($message); } private function determineBestFormat() diff --git a/src/Symfony/Component/Console/Helper/ProgressIndicator.php b/src/Symfony/Component/Console/Helper/ProgressIndicator.php index 8c8a49c44b910..ccf9771bcf9e4 100644 --- a/src/Symfony/Component/Console/Helper/ProgressIndicator.php +++ b/src/Symfony/Component/Console/Helper/ProgressIndicator.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Console\Helper; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; use Symfony\Component\Console\Output\OutputInterface; /** @@ -53,7 +55,7 @@ public function __construct(OutputInterface $output, $format = null, $indicatorC $indicatorValues = array_values($indicatorValues); if (2 > count($indicatorValues)) { - throw new \InvalidArgumentException('Must have at least 2 indicator value characters.'); + throw new InvalidArgumentException('Must have at least 2 indicator value characters.'); } $this->format = self::getFormatDefinition($format); @@ -118,7 +120,7 @@ public function getCurrentValue() public function start($message) { if ($this->started) { - throw new \LogicException('Progress indicator already started.'); + throw new LogicException('Progress indicator already started.'); } $this->message = $message; @@ -137,7 +139,7 @@ public function start($message) public function advance() { if (!$this->started) { - throw new \LogicException('Progress indicator has not yet been started.'); + throw new LogicException('Progress indicator has not yet been started.'); } if (!$this->output->isDecorated()) { @@ -164,7 +166,7 @@ public function advance() public function finish($message) { if (!$this->started) { - throw new \LogicException('Progress indicator has not yet been started.'); + throw new LogicException('Progress indicator has not yet been started.'); } $this->message = $message; diff --git a/src/Symfony/Component/Console/Helper/Table.php b/src/Symfony/Component/Console/Helper/Table.php index 1f103ad144ca5..dc038e4707c16 100644 --- a/src/Symfony/Component/Console/Helper/Table.php +++ b/src/Symfony/Component/Console/Helper/Table.php @@ -162,7 +162,7 @@ public function setColumnStyle($columnIndex, $name) } elseif (isset(self::$styles[$name])) { $this->columnStyles[$columnIndex] = self::$styles[$name]; } else { - throw new \InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); + throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); } return $this; diff --git a/src/Symfony/Component/Console/Output/StreamOutput.php b/src/Symfony/Component/Console/Output/StreamOutput.php index 62d04c0e0d966..098966286eeb5 100644 --- a/src/Symfony/Component/Console/Output/StreamOutput.php +++ b/src/Symfony/Component/Console/Output/StreamOutput.php @@ -85,7 +85,7 @@ protected function doWrite($message, $newline) * * Colorization is disabled if not supported by the stream: * - * - Windows without Ansicon, ConEmu or Mintty + * - Windows before 10.0.10586 without Ansicon, ConEmu or Mintty * - non tty consoles * * @return bool true if the stream supports colorization, false otherwise @@ -93,7 +93,11 @@ protected function doWrite($message, $newline) protected function hasColorSupport() { if (DIRECTORY_SEPARATOR === '\\') { - return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); + return + 0 >= version_compare('10.0.10586', PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD) + || false !== getenv('ANSICON') + || 'ON' === getenv('ConEmuANSI') + || 'xterm' === getenv('TERM'); } return function_exists('posix_isatty') && @posix_isatty($this->stream); diff --git a/src/Symfony/Component/Console/Style/SymfonyStyle.php b/src/Symfony/Component/Console/Style/SymfonyStyle.php index 47d7d1249d128..6086918ac7840 100644 --- a/src/Symfony/Component/Console/Style/SymfonyStyle.php +++ b/src/Symfony/Component/Console/Style/SymfonyStyle.php @@ -18,6 +18,7 @@ use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Helper\SymfonyQuestionHelper; use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Helper\TableCell; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\BufferedOutput; use Symfony\Component\Console\Output\OutputInterface; @@ -213,7 +214,16 @@ public function caution($message) */ public function table(array $headers, array $rows) { - $headers = array_map(function ($value) { return sprintf('%s', $value); }, $headers); + array_walk_recursive($headers, function (&$value) { + if ($value instanceof TableCell) { + $value = new TableCell(sprintf('%s', $value), array( + 'colspan' => $value->getColspan(), + 'rowspan' => $value->getRowspan(), + )); + } else { + $value = sprintf('%s', $value); + } + }); $table = new Table($this); $table->setHeaders($headers); diff --git a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_8.php b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_8.php new file mode 100644 index 0000000000000..0244fd27256f2 --- /dev/null +++ b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_8.php @@ -0,0 +1,26 @@ + 3))), + array('ISBN', 'Title', 'Author'), + ); + + $rows = array( + array( + '978-0521567817', + 'De Monarchia', + new TableCell("Dante Alighieri\nspans multiple rows", array('rowspan' => 2)), + ), + array('978-0804169127', 'Divine Comedy'), + ); + + $output = new SymfonyStyleWithForcedLineLength($input, $output); + $output->table($headers, $rows); +}; diff --git a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_8.txt b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_8.txt new file mode 100644 index 0000000000000..005b846eae491 --- /dev/null +++ b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_8.txt @@ -0,0 +1,9 @@ + ---------------- --------------- --------------------- + Main table title + ---------------- --------------- --------------------- + ISBN Title Author + ---------------- --------------- --------------------- + 978-0521567817 De Monarchia Dante Alighieri + 978-0804169127 Divine Comedy spans multiple rows + ---------------- --------------- --------------------- + diff --git a/src/Symfony/Component/Console/Tests/Helper/HelperTest.php b/src/Symfony/Component/Console/Tests/Helper/HelperTest.php new file mode 100644 index 0000000000000..33fa22051a05d --- /dev/null +++ b/src/Symfony/Component/Console/Tests/Helper/HelperTest.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\Helper; + +class HelperTest extends \PHPUnit_Framework_TestCase +{ + public function formatTimeProvider() + { + return array( + array(0, '< 1 sec'), + array(1, '1 sec'), + array(2, '2 secs'), + array(59, '59 secs'), + array(60, '1 min'), + array(61, '1 min'), + array(119, '1 min'), + array(120, '2 mins'), + array(121, '2 mins'), + array(3599, '59 mins'), + array(3600, '1 hr'), + array(7199, '1 hr'), + array(7200, '2 hrs'), + array(7201, '2 hrs'), + array(86399, '23 hrs'), + array(86400, '1 day'), + array(86401, '1 day'), + array(172799, '1 day'), + array(172800, '2 days'), + array(172801, '2 days'), + ); + } + + /** + * @dataProvider formatTimeProvider + * + * @param int $secs + * @param string $expectedFormat + */ + public function testFormatTime($secs, $expectedFormat) + { + $this->assertEquals($expectedFormat, Helper::formatTime($secs)); + } +} diff --git a/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php b/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php index 4b25d1b72a46b..261908b542307 100644 --- a/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php @@ -233,7 +233,7 @@ public function testOverwriteWithShorterLine() $this->generateOutput(' 0/50 [>---------------------------] 0%'). $this->generateOutput(' 0/50 [>---------------------------] 0%'). $this->generateOutput(' 1/50 [>---------------------------] 2%'). - $this->generateOutput(' 2/50 [=>--------------------------] '), + $this->generateOutput(' 2/50 [=>--------------------------]'), stream_get_contents($output->getStream()) ); } @@ -353,7 +353,7 @@ public function testClear() $this->assertEquals( $this->generateOutput(' 0/50 [>---------------------------] 0%'). $this->generateOutput(' 25/50 [==============>-------------] 50%'). - $this->generateOutput(' '), + $this->generateOutput(''), stream_get_contents($output->getStream()) ); } @@ -551,9 +551,9 @@ public function testMultilineFormat() rewind($output->getStream()); $this->assertEquals( $this->generateOutput(">---------------------------\nfoobar"). - $this->generateOutput("=========>------------------\nfoobar "). - $this->generateOutput(" \n "). - $this->generateOutput("============================\nfoobar "), + $this->generateOutput("=========>------------------\nfoobar"). + "\x0D\x1B[2K\x1B[1A\x1B[2K". + $this->generateOutput("============================\nfoobar"), stream_get_contents($output->getStream()) ); } @@ -585,17 +585,17 @@ public function testAnsiColorsAndEmojis() $this->generateOutput( " \033[44;37m Starting the demo... fingers crossed \033[0m\n". ' 0/15 '.$progress.str_repeat($empty, 26)." 0%\n". - " \xf0\x9f\x8f\x81 1 sec \033[44;37m 0 B \033[0m" + " \xf0\x9f\x8f\x81 < 1 sec \033[44;37m 0 B \033[0m" ). $this->generateOutput( " \033[44;37m Looks good to me... \033[0m\n". ' 4/15 '.str_repeat($done, 7).$progress.str_repeat($empty, 19)." 26%\n". - " \xf0\x9f\x8f\x81 1 sec \033[41;37m 97 KiB \033[0m" + " \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 97 KiB \033[0m" ). $this->generateOutput( " \033[44;37m Thanks, bye \033[0m\n". ' 15/15 '.str_repeat($done, 28)." 100%\n". - " \xf0\x9f\x8f\x81 1 sec \033[41;37m 195 KiB \033[0m" + " \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 195 KiB \033[0m" ), stream_get_contents($output->getStream()) ); @@ -659,6 +659,6 @@ protected function generateOutput($expected) { $count = substr_count($expected, "\n"); - return "\x0D".($count ? sprintf("\033[%dA", $count) : '').$expected; + return "\x0D\x1B[2K".($count ? str_repeat("\x1B[1A\x1B[2K", $count) : '').$expected; } } diff --git a/src/Symfony/Component/Debug/DebugClassLoader.php b/src/Symfony/Component/Debug/DebugClassLoader.php index 93b0ff5e4e20c..636b89c381ac2 100644 --- a/src/Symfony/Component/Debug/DebugClassLoader.php +++ b/src/Symfony/Component/Debug/DebugClassLoader.php @@ -51,15 +51,25 @@ public function __construct($classLoader) } if (!isset(self::$caseCheck)) { - if(!file_exists(strtolower(__FILE__))) { + $file = file_exists(__FILE__) ? __FILE__ : rtrim(realpath('.'), DIRECTORY_SEPARATOR); + $i = strrpos($file, DIRECTORY_SEPARATOR); + $dir = substr($file, 0, 1 + $i); + $file = substr($file, 1 + $i); + $test = strtoupper($file) === $file ? strtolower($file) : strtoupper($file); + $test = realpath($dir.$test); + + if (false === $test || false === $i) { // filesystem is case sensitive self::$caseCheck = 0; - } elseif(realpath(strtolower(__FILE__)) === __FILE__) { - // filesystem is not case sensitive + } elseif (substr($test, -strlen($file)) === $file) { + // filesystem is case insensitive and realpath() normalizes the case of characters self::$caseCheck = 1; - } else { - // filesystem is not case sensitive AND realpath() fails to normalize case + } elseif (false !== stripos(PHP_OS, 'darwin')) { + // on MacOSX, HFS+ is case insensitive but realpath() doesn't normalize the case of characters self::$caseCheck = 2; + } else { + // filesystem case checks failed, fallback to disabling them + self::$caseCheck = 0; } } } diff --git a/src/Symfony/Component/DependencyInjection/Alias.php b/src/Symfony/Component/DependencyInjection/Alias.php index 5a6c2feda7f4e..eaf7f00ccd446 100644 --- a/src/Symfony/Component/DependencyInjection/Alias.php +++ b/src/Symfony/Component/DependencyInjection/Alias.php @@ -17,8 +17,6 @@ class Alias private $public; /** - * Constructor. - * * @param string $id Alias identifier * @param bool $public If this alias is public */ diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php index 5120eb6215c1a..42b491c7c70e8 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php @@ -34,8 +34,6 @@ class AnalyzeServiceReferencesPass implements RepeatablePassInterface private $onlyConstructorArguments; /** - * Constructor. - * * @param bool $onlyConstructorArguments Sets this Service Reference pass to ignore method calls */ public function __construct($onlyConstructorArguments = false) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index b46635dca615a..4cef7df71401a 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -34,19 +34,32 @@ class AutowirePass implements CompilerPassInterface */ public function process(ContainerBuilder $container) { - $this->container = $container; - foreach ($container->getDefinitions() as $id => $definition) { - if ($definition->isAutowired()) { - $this->completeDefinition($id, $definition); + $throwingAutoloader = function ($class) { throw new \ReflectionException(sprintf('Class %s does not exist', $class)); }; + spl_autoload_register($throwingAutoloader); + + try { + $this->container = $container; + foreach ($container->getDefinitions() as $id => $definition) { + if ($definition->isAutowired()) { + $this->completeDefinition($id, $definition); + } } + } catch (\Error $e) { + } catch (\Exception $e) { } + spl_autoload_unregister($throwingAutoloader); + // Free memory and remove circular reference to container $this->container = null; $this->reflectionClasses = array(); $this->definedTypes = array(); $this->types = null; $this->notGuessableTypes = array(); + + if (isset($e)) { + throw $e; + } } /** @@ -107,11 +120,11 @@ private function completeDefinition($id, Definition $definition) } } } - } catch (\ReflectionException $reflectionException) { + } catch (\ReflectionException $e) { // Typehint against a non-existing class if (!$parameter->isDefaultValueAvailable()) { - throw new RuntimeException(sprintf('Cannot autowire argument %s for %s because the type-hinted class does not exist (%s).', $index + 1, $definition->getClass(), $reflectionException->getMessage()), 0, $reflectionException); + throw new RuntimeException(sprintf('Cannot autowire argument %s for %s because the type-hinted class does not exist (%s).', $index + 1, $definition->getClass(), $e->getMessage()), 0, $e); } $value = $parameter->getDefaultValue(); @@ -228,7 +241,7 @@ private function createAutowiredDefinition(\ReflectionClass $typeHint, $id) * @param string $id * @param Definition $definition * - * @return \ReflectionClass|null + * @return \ReflectionClass|false */ private function getReflectionClass($id, Definition $definition) { @@ -238,15 +251,17 @@ private function getReflectionClass($id, Definition $definition) // Cannot use reflection if the class isn't set if (!$class = $definition->getClass()) { - return; + return false; } $class = $this->container->getParameterBag()->resolveValue($class); try { - return $this->reflectionClasses[$id] = new \ReflectionClass($class); - } catch (\ReflectionException $reflectionException) { - // return null + $reflector = new \ReflectionClass($class); + } catch (\ReflectionException $e) { + $reflector = false; } + + return $this->reflectionClasses[$id] = $reflector; } } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php b/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php index 4e4c2cdaba5b9..1f6304ee82e13 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php @@ -25,9 +25,6 @@ class Compiler private $loggingFormatter; private $serviceReferenceGraph; - /** - * Constructor. - */ public function __construct() { $this->passConfig = new PassConfig(); diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php index 246529d865cd3..ffb8c01f0f762 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php @@ -35,9 +35,6 @@ class PassConfig private $optimizationPasses; private $removingPasses; - /** - * Constructor. - */ public function __construct() { $this->mergePass = new MergeExtensionConfigurationPass(); @@ -58,8 +55,8 @@ public function __construct() $this->removingPasses = array( new RemovePrivateAliasesPass(), - new RemoveAbstractDefinitionsPass(), new ReplaceAliasByActualDefinitionPass(), + new RemoveAbstractDefinitionsPass(), new RepeatedPass(array( new AnalyzeServiceReferencesPass(), new InlineServiceDefinitionsPass(), @@ -102,8 +99,7 @@ public function addPass(CompilerPassInterface $pass, $type = self::TYPE_BEFORE_O throw new InvalidArgumentException(sprintf('Invalid type "%s".', $type)); } - $passes = &$this->$property; - $passes[] = $pass; + $this->{$property}[] = $pass; } /** diff --git a/src/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php b/src/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php index e34b0681e5389..508a8978ea68b 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php @@ -32,8 +32,6 @@ class RepeatedPass implements CompilerPassInterface private $passes; /** - * Constructor. - * * @param RepeatablePassInterface[] $passes An array of RepeatablePassInterface objects * * @throws InvalidArgumentException when the passes don't implement RepeatablePassInterface diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php index c5332fcd8d987..e80d3ccbf7b53 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php @@ -25,7 +25,6 @@ class ReplaceAliasByActualDefinitionPass implements CompilerPassInterface { private $compiler; private $formatter; - private $sourceId; /** * Process the Container to replace aliases with service definitions. @@ -36,114 +35,110 @@ class ReplaceAliasByActualDefinitionPass implements CompilerPassInterface */ public function process(ContainerBuilder $container) { + // Setup $this->compiler = $container->getCompiler(); $this->formatter = $this->compiler->getLoggingFormatter(); - - foreach ($container->getAliases() as $id => $alias) { - $aliasId = (string) $alias; - + // First collect all alias targets that need to be replaced + $seenAliasTargets = array(); + $replacements = array(); + foreach ($container->getAliases() as $definitionId => $target) { + $targetId = (string) $target; + // Special case: leave this target alone + if ('service_container' === $targetId) { + continue; + } + // Check if target needs to be replaces + if (isset($replacements[$targetId])) { + $container->setAlias($definitionId, $replacements[$targetId]); + } + // No neeed to process the same target twice + if (isset($seenAliasTargets[$targetId])) { + continue; + } + // Process new target + $seenAliasTargets[$targetId] = true; try { - $definition = $container->getDefinition($aliasId); + $definition = $container->getDefinition($targetId); } catch (InvalidArgumentException $e) { - throw new InvalidArgumentException(sprintf('Unable to replace alias "%s" with actual definition "%s".', $id, $alias), null, $e); + throw new InvalidArgumentException(sprintf('Unable to replace alias "%s" with actual definition "%s".', $definitionId, $targetId), null, $e); } - if ($definition->isPublic()) { continue; } - + // Remove private definition and schedule for replacement $definition->setPublic(true); - $container->setDefinition($id, $definition); - $container->removeDefinition($aliasId); - - $this->updateReferences($container, $aliasId, $id); - - // we have to restart the process due to concurrent modification of - // the container - $this->process($container); - - break; - } - } - - /** - * Updates references to remove aliases. - * - * @param ContainerBuilder $container The container - * @param string $currentId The alias identifier being replaced - * @param string $newId The id of the service the alias points to - */ - private function updateReferences($container, $currentId, $newId) - { - foreach ($container->getAliases() as $id => $alias) { - if ($currentId === (string) $alias) { - $container->setAlias($id, $newId); - } + $container->setDefinition($definitionId, $definition); + $container->removeDefinition($targetId); + $replacements[$targetId] = $definitionId; } - foreach ($container->getDefinitions() as $id => $definition) { - $this->sourceId = $id; - - $definition->setArguments( - $this->updateArgumentReferences($definition->getArguments(), $currentId, $newId) - ); - - $definition->setMethodCalls( - $this->updateArgumentReferences($definition->getMethodCalls(), $currentId, $newId) - ); - - $definition->setProperties( - $this->updateArgumentReferences($definition->getProperties(), $currentId, $newId) - ); - - $definition->setFactoryService($this->updateFactoryServiceReference($definition->getFactoryService(false), $currentId, $newId), false); - $definition->setFactory($this->updateFactoryReference($definition->getFactory(), $currentId, $newId)); + // Now replace target instances in all definitions + foreach ($container->getDefinitions() as $definitionId => $definition) { + $definition->setArguments($this->updateArgumentReferences($replacements, $definitionId, $definition->getArguments())); + $definition->setMethodCalls($this->updateArgumentReferences($replacements, $definitionId, $definition->getMethodCalls())); + $definition->setProperties($this->updateArgumentReferences($replacements, $definitionId, $definition->getProperties())); + $definition->setFactoryService($this->updateFactoryReferenceId($replacements, $definition->getFactoryService(false)), false); + $definition->setFactory($this->updateFactoryReference($replacements, $definition->getFactory())); } } /** - * Updates argument references. + * Recursively updates references in an array. * - * @param array $arguments An array of Arguments - * @param string $currentId The alias identifier - * @param string $newId The identifier the alias points to + * @param array $replacements Table of aliases to replace + * @param string $definitionId Identifier of this definition + * @param array $arguments Where to replace the aliases * * @return array */ - private function updateArgumentReferences(array $arguments, $currentId, $newId) + private function updateArgumentReferences(array $replacements, $definitionId, array $arguments) { foreach ($arguments as $k => $argument) { + // Handle recursion step if (is_array($argument)) { - $arguments[$k] = $this->updateArgumentReferences($argument, $currentId, $newId); - } elseif ($argument instanceof Reference) { - if ($currentId === (string) $argument) { - $arguments[$k] = new Reference($newId, $argument->getInvalidBehavior()); - $this->compiler->addLogMessage($this->formatter->formatUpdateReference($this, $this->sourceId, $currentId, $newId)); - } + $arguments[$k] = $this->updateArgumentReferences($replacements, $definitionId, $argument); + continue; } + // Skip arguments that don't need replacement + if (!$argument instanceof Reference) { + continue; + } + $referenceId = (string) $argument; + if (!isset($replacements[$referenceId])) { + continue; + } + // Perform the replacement + $newId = $replacements[$referenceId]; + $arguments[$k] = new Reference($newId, $argument->getInvalidBehavior()); + $this->compiler->addLogMessage($this->formatter->formatUpdateReference($this, $definitionId, $referenceId, $newId)); } return $arguments; } - private function updateFactoryServiceReference($factoryService, $currentId, $newId) + /** + * Returns the updated reference for the factory service. + * + * @param array $replacements Table of aliases to replace + * @param string|null $referenceId Factory service reference identifier + * + * @return string|null + */ + private function updateFactoryReferenceId(array $replacements, $referenceId) { - if (null === $factoryService) { + if (null === $referenceId) { return; } - return $currentId === $factoryService ? $newId : $factoryService; + return isset($replacements[$referenceId]) ? $replacements[$referenceId] : $referenceId; } - private function updateFactoryReference($factory, $currentId, $newId) + private function updateFactoryReference(array $replacements, $factory) { - if (null === $factory || !is_array($factory) || !$factory[0] instanceof Reference) { - return $factory; + if (is_array($factory) && $factory[0] instanceof Reference && isset($replacements[$referenceId = (string) $factory[0]])) { + $factory[0] = new Reference($replacements[$referenceId], $factory[0]->getInvalidBehavior()); } - if ($currentId === (string) $factory[0]) { - $factory[0] = new Reference($newId, $factory[0]->getInvalidBehavior()); - } return $factory; } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphEdge.php b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphEdge.php index 6a3e2ea5697d3..056be7fa3f5d4 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphEdge.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphEdge.php @@ -25,8 +25,6 @@ class ServiceReferenceGraphEdge private $value; /** - * Constructor. - * * @param ServiceReferenceGraphNode $sourceNode * @param ServiceReferenceGraphNode $destNode * @param string $value diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.php b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.php index c49c932575f03..e5718b2b6d59a 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.php @@ -29,8 +29,6 @@ class ServiceReferenceGraphNode private $value; /** - * Constructor. - * * @param string $id The node identifier * @param mixed $value The node value */ diff --git a/src/Symfony/Component/DependencyInjection/Container.php b/src/Symfony/Component/DependencyInjection/Container.php index 3305fc98c6dd3..51ea1bc2a3130 100644 --- a/src/Symfony/Component/DependencyInjection/Container.php +++ b/src/Symfony/Component/DependencyInjection/Container.php @@ -78,8 +78,6 @@ class Container implements IntrospectableContainerInterface, ResettableContainer private $underscoreMap = array('_' => '', '.' => '_', '\\' => '_'); /** - * Constructor. - * * @param ParameterBagInterface $parameterBag A ParameterBagInterface instance */ public function __construct(ParameterBagInterface $parameterBag = null) diff --git a/src/Symfony/Component/DependencyInjection/ContainerAware.php b/src/Symfony/Component/DependencyInjection/ContainerAware.php index 5686bf81d2c02..f3f2a5065c311 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerAware.php +++ b/src/Symfony/Component/DependencyInjection/ContainerAware.php @@ -26,9 +26,7 @@ abstract class ContainerAware implements ContainerAwareInterface protected $container; /** - * Sets the container. - * - * @param ContainerInterface|null $container A ContainerInterface instance or null + * {@inheritdoc} */ public function setContainer(ContainerInterface $container = null) { diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php index bda42963219a2..895b37459cd27 100644 --- a/src/Symfony/Component/DependencyInjection/Definition.php +++ b/src/Symfony/Component/DependencyInjection/Definition.php @@ -47,8 +47,6 @@ class Definition protected $arguments; /** - * Constructor. - * * @param string|null $class The service class * @param array $arguments An array of arguments to pass to the service constructor */ diff --git a/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php b/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php index a434b562da3ba..83ee0a0e7d303 100644 --- a/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php +++ b/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php @@ -25,8 +25,6 @@ class DefinitionDecorator extends Definition private $changes = array(); /** - * Constructor. - * * @param string $parent The id of Definition instance to decorate. */ public function __construct($parent) diff --git a/src/Symfony/Component/DependencyInjection/Dumper/Dumper.php b/src/Symfony/Component/DependencyInjection/Dumper/Dumper.php index 4b9d586f2c934..a39a5c744ba78 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/Dumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/Dumper.php @@ -23,8 +23,6 @@ abstract class Dumper implements DumperInterface protected $container; /** - * Constructor. - * * @param ContainerBuilder $container The service container to dump */ public function __construct(ContainerBuilder $container) diff --git a/src/Symfony/Component/DependencyInjection/Exception/ParameterNotFoundException.php b/src/Symfony/Component/DependencyInjection/Exception/ParameterNotFoundException.php index b529f0fe83d22..ab7b86d5acc90 100644 --- a/src/Symfony/Component/DependencyInjection/Exception/ParameterNotFoundException.php +++ b/src/Symfony/Component/DependencyInjection/Exception/ParameterNotFoundException.php @@ -24,8 +24,6 @@ class ParameterNotFoundException extends InvalidArgumentException private $alternatives; /** - * Constructor. - * * @param string $key The requested parameter key * @param string $sourceId The service id that references the non-existent parameter * @param string $sourceKey The parameter key that references the non-existent parameter diff --git a/src/Symfony/Component/DependencyInjection/Extension/Extension.php b/src/Symfony/Component/DependencyInjection/Extension/Extension.php index 20ea1002cb82c..ced39f7281b6c 100644 --- a/src/Symfony/Component/DependencyInjection/Extension/Extension.php +++ b/src/Symfony/Component/DependencyInjection/Extension/Extension.php @@ -27,9 +27,7 @@ abstract class Extension implements ExtensionInterface, ConfigurationExtensionInterface { /** - * Returns the base path for the XSD files. - * - * @return string The XSD base path + * {@inheritdoc} */ public function getXsdValidationBasePath() { @@ -37,9 +35,7 @@ public function getXsdValidationBasePath() } /** - * Returns the namespace to be used for this extension (XML namespace). - * - * @return string The XML namespace + * {@inheritdoc} */ public function getNamespace() { diff --git a/src/Symfony/Component/DependencyInjection/Loader/ClosureLoader.php b/src/Symfony/Component/DependencyInjection/Loader/ClosureLoader.php index a5b4e5ad240e5..df70cdf44f283 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/ClosureLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/ClosureLoader.php @@ -26,8 +26,6 @@ class ClosureLoader extends Loader private $container; /** - * Constructor. - * * @param ContainerBuilder $container A ContainerBuilder instance */ public function __construct(ContainerBuilder $container) diff --git a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php index d71eecf74156f..90cd6bcfafa4d 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php @@ -25,8 +25,6 @@ abstract class FileLoader extends BaseFileLoader protected $container; /** - * Constructor. - * * @param ContainerBuilder $container A ContainerBuilder instance * @param FileLocatorInterface $locator A FileLocator instance */ diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index c19b86d5660c1..8da4bcd8e2b2a 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -325,11 +325,7 @@ private function processAnonymousServices(\DOMDocument $xml, $file) // give it a unique name $id = sprintf('%s_%d', hash('sha256', $file), ++$count); $node->setAttribute('id', $id); - - if ($services = $this->getChildren($node, 'service')) { - $definitions[$id] = array($node, $file, true); - $services[0]->setAttribute('id', $id); - } + $definitions[$id] = array($node, $file, true); } } diff --git a/src/Symfony/Component/DependencyInjection/Parameter.php b/src/Symfony/Component/DependencyInjection/Parameter.php index 5431ed8221cf4..cac6f6c4c2264 100644 --- a/src/Symfony/Component/DependencyInjection/Parameter.php +++ b/src/Symfony/Component/DependencyInjection/Parameter.php @@ -21,8 +21,6 @@ class Parameter private $id; /** - * Constructor. - * * @param string $id The parameter key */ public function __construct($id) @@ -31,8 +29,6 @@ public function __construct($id) } /** - * __toString. - * * @return string The parameter key */ public function __toString() diff --git a/src/Symfony/Component/DependencyInjection/ParameterBag/FrozenParameterBag.php b/src/Symfony/Component/DependencyInjection/ParameterBag/FrozenParameterBag.php index d9fe9eceb69b9..ad65ad960f527 100644 --- a/src/Symfony/Component/DependencyInjection/ParameterBag/FrozenParameterBag.php +++ b/src/Symfony/Component/DependencyInjection/ParameterBag/FrozenParameterBag.php @@ -21,8 +21,6 @@ class FrozenParameterBag extends ParameterBag { /** - * Constructor. - * * For performance reasons, the constructor assumes that * all keys are already lowercased. * diff --git a/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php b/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php index 64a7789d1a859..0611b1f69e322 100644 --- a/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php +++ b/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php @@ -26,8 +26,6 @@ class ParameterBag implements ParameterBagInterface protected $resolved = false; /** - * Constructor. - * * @param array $parameters An array of parameters */ public function __construct(array $parameters = array()) @@ -56,9 +54,7 @@ public function add(array $parameters) } /** - * Gets the service container parameters. - * - * @return array An array of parameters + * {@inheritdoc} */ public function all() { @@ -66,13 +62,7 @@ public function all() } /** - * Gets a service container parameter. - * - * @param string $name The parameter name - * - * @return mixed The parameter value - * - * @throws ParameterNotFoundException if the parameter is not defined + * {@inheritdoc} */ public function get($name) { @@ -109,11 +99,7 @@ public function set($name, $value) } /** - * Returns true if a parameter name is defined. - * - * @param string $name The parameter name - * - * @return bool true if the parameter name is defined, false otherwise + * {@inheritdoc} */ public function has($name) { @@ -131,7 +117,7 @@ public function remove($name) } /** - * Replaces parameter placeholders (%name%) by their values for all parameters. + * {@inheritdoc} */ public function resolve() { @@ -266,6 +252,9 @@ public function escapeValue($value) return $value; } + /** + * {@inheritdoc} + */ public function unescapeValue($value) { if (is_string($value)) { diff --git a/src/Symfony/Component/DependencyInjection/Reference.php b/src/Symfony/Component/DependencyInjection/Reference.php index 572062be9cccf..cb2445023da01 100644 --- a/src/Symfony/Component/DependencyInjection/Reference.php +++ b/src/Symfony/Component/DependencyInjection/Reference.php @@ -23,8 +23,6 @@ class Reference private $strict; /** - * Constructor. - * * Note: The $strict parameter is deprecated since version 2.8 and will be removed in 3.0. * * @param string $id The service identifier @@ -41,8 +39,6 @@ public function __construct($id, $invalidBehavior = ContainerInterface::EXCEPTIO } /** - * __toString. - * * @return string The service identifier */ public function __toString() diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index 4a97456f99ba1..1c61f05b0b7b6 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -268,6 +268,21 @@ public function testClassNotFoundThrowsException() $pass->process($container); } + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Cannot autowire argument 2 for Symfony\Component\DependencyInjection\Tests\Compiler\BadParentTypeHintedArgument because the type-hinted class does not exist (Class Symfony\Component\DependencyInjection\Tests\Compiler\OptionalServiceClass does not exist). + */ + public function testParentClassNotFoundThrowsException() + { + $container = new ContainerBuilder(); + + $aDefinition = $container->register('a', __NAMESPACE__.'\BadParentTypeHintedArgument'); + $aDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + } + public function testDontUseAbstractServices() { $container = new ContainerBuilder(); @@ -397,6 +412,21 @@ public function testOptionalScalarArgsNotPassedIfLast() $definition->getArguments() ); } + + public function testIgnoreServiceWithClassNotExisting() + { + $container = new ContainerBuilder(); + + $container->register('class_not_exist', __NAMESPACE__.'\OptionalServiceClass'); + + $barDefinition = $container->register('bar', __NAMESPACE__.'\Bar'); + $barDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertTrue($container->hasDefinition('bar')); + } } class Foo @@ -509,6 +539,12 @@ public function __construct(Dunglas $k, NotARealClass $r) { } } +class BadParentTypeHintedArgument +{ + public function __construct(Dunglas $k, OptionalServiceClass $r) + { + } +} class NotGuessableArgument { public function __construct(Foo $k) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/OptionalServiceClass.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/OptionalServiceClass.php new file mode 100644 index 0000000000000..7e9238f22301b --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/OptionalServiceClass.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use Symfony\Bug\NotExistClass; + +class OptionalServiceClass extends NotExistClass +{ +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ReplaceAliasByActualDefinitionPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ReplaceAliasByActualDefinitionPassTest.php index be58a1a175967..7cb63c6613663 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ReplaceAliasByActualDefinitionPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ReplaceAliasByActualDefinitionPassTest.php @@ -36,6 +36,8 @@ public function testProcess() $container->setAlias('a_alias', 'a'); $container->setAlias('b_alias', 'b'); + $container->setAlias('container', 'service_container'); + $this->process($container); $this->assertTrue($container->has('a'), '->process() does nothing to public definitions.'); @@ -47,6 +49,7 @@ public function testProcess() ); $this->assertSame('b_alias', $aDefinition->getFactoryService(false)); + $this->assertTrue($container->has('container')); $resolvedFactory = $aDefinition->getFactory(false); $this->assertSame('b_alias', (string) $resolvedFactory[0]); diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 18f7b67f46a5b..8e80c47205908 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -61,24 +61,27 @@ public function testDefinitions() public function testCreateDeprecatedService() { - $definition = new Definition('stdClass'); - $definition->setDeprecated(true); + $deprecations = array(); + set_error_handler(function ($type, $msg) use (&$deprecations) { + if (E_USER_DEPRECATED !== $type) { + restore_error_handler(); - $that = $this; - $wasTriggered = false; + return call_user_func_array('PHPUnit_Util_ErrorHandler::handleError', func_get_args()); + } - set_error_handler(function ($errno, $errstr) use ($that, &$wasTriggered) { - $that->assertSame(E_USER_DEPRECATED, $errno); - $that->assertSame('The "deprecated_foo" service is deprecated. You should stop using it, as it will soon be removed.', $errstr); - $wasTriggered = true; + $deprecations[] = $msg; }); + $definition = new Definition('stdClass'); + $definition->setDeprecated(true); + $builder = new ContainerBuilder(); $builder->createService($definition, 'deprecated_foo'); restore_error_handler(); - $this->assertTrue($wasTriggered); + $this->assertCount(1, $deprecations); + $this->assertContains('The "deprecated_foo" service is deprecated. You should stop using it, as it will soon be removed.', $deprecations[0]); } public function testRegister() @@ -786,6 +789,21 @@ public function testExtensionConfig() $this->assertEquals(array($second, $first), $configs); } + public function testAbstractAlias() + { + $container = new ContainerBuilder(); + + $abstract = new Definition('AbstractClass'); + $abstract->setAbstract(true); + + $container->setDefinition('abstract_service', $abstract); + $container->setAlias('abstract_alias', 'abstract_service'); + + $container->compile(); + + $this->assertSame('abstract_service', (string) $container->getAlias('abstract_alias')); + } + public function testLazyLoadedService() { $loader = new ClosureLoader($container = new ContainerBuilder()); diff --git a/src/Symfony/Component/DependencyInjection/Tests/CrossCheckTest.php b/src/Symfony/Component/DependencyInjection/Tests/CrossCheckTest.php index 692d73dbcdc2e..7905e7f3c6b9c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/CrossCheckTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/CrossCheckTest.php @@ -73,24 +73,17 @@ public function testCrossCheck($fixture, $type) public function crossCheckLoadersDumpers() { - $tests = array( + return array( array('services1.xml', 'xml'), array('services2.xml', 'xml'), array('services6.xml', 'xml'), array('services8.xml', 'xml'), array('services9.xml', 'xml'), + array('services1.yml', 'yaml'), + array('services2.yml', 'yaml'), + array('services6.yml', 'yaml'), + array('services8.yml', 'yaml'), + array('services9.yml', 'yaml'), ); - - if (class_exists('Symfony\Component\Yaml\Yaml')) { - $tests = array_merge($tests, array( - array('services1.yml', 'yaml'), - array('services2.yml', 'yaml'), - array('services6.yml', 'yaml'), - array('services8.yml', 'yaml'), - array('services9.yml', 'yaml'), - )); - } - - return $tests; } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 05d846b353654..6f600166ff1d9 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -16,6 +16,8 @@ use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Variable; +use Symfony\Component\ExpressionLanguage\Expression; class PhpDumperTest extends \PHPUnit_Framework_TestCase { @@ -85,14 +87,25 @@ public function testDumpRelativeDir() } /** + * @dataProvider provideInvalidParameters * @expectedException \InvalidArgumentException */ - public function testExportParameters() + public function testExportParameters($parameters) { - $dumper = new PhpDumper(new ContainerBuilder(new ParameterBag(array('foo' => new Reference('foo'))))); + $dumper = new PhpDumper(new ContainerBuilder(new ParameterBag($parameters))); $dumper->dump(); } + public function provideInvalidParameters() + { + return array( + array(array('foo' => new Definition('stdClass'))), + array(array('foo' => new Expression('service("foo").foo() ~ (container.hasparameter("foo") ? parameter("foo") : "default")'))), + array(array('foo' => new Reference('foo'))), + array(array('foo' => new Variable('foo'))), + ); + } + public function testAddParameters() { $container = include self::$fixturesPath.'/containers/container8.php'; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services5.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services5.xml index acb93e748e483..347df977dd26b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services5.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services5.xml @@ -14,8 +14,12 @@ - + + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index a49925f9298ea..b0f3535308fdd 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -163,7 +163,7 @@ public function testLoadAnonymousServices() $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); $loader->load('services5.xml'); $services = $container->getDefinitions(); - $this->assertCount(4, $services, '->load() attributes unique ids to anonymous services'); + $this->assertCount(6, $services, '->load() attributes unique ids to anonymous services'); // anonymous service as an argument $args = $services['foo']->getArguments(); @@ -172,6 +172,7 @@ public function testLoadAnonymousServices() $this->assertTrue(isset($services[(string) $args[0]]), '->load() makes a reference to the created ones'); $inner = $services[(string) $args[0]]; $this->assertEquals('BarClass', $inner->getClass(), '->load() uses the same configuration as for the anonymous ones'); + $this->assertFalse($inner->isPublic()); // inner anonymous services $args = $inner->getArguments(); @@ -188,7 +189,25 @@ public function testLoadAnonymousServices() $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Reference', $property, '->load() converts anonymous services to references to "normal" services'); $this->assertTrue(isset($services[(string) $property]), '->load() makes a reference to the created ones'); $inner = $services[(string) $property]; - $this->assertEquals('BazClass', $inner->getClass(), '->load() uses the same configuration as for the anonymous ones'); + $this->assertEquals('BuzClass', $inner->getClass(), '->load() uses the same configuration as for the anonymous ones'); + $this->assertFalse($inner->isPublic()); + + // "wild" service + $service = $container->findTaggedServiceIds('biz_tag'); + $this->assertCount(1, $service); + + foreach ($service as $id => $tag) { + $service = $container->getDefinition($id); + } + $this->assertEquals('BizClass', $service->getClass(), '->load() uses the same configuration as for the anonymous ones'); + $this->assertFalse($service->isPublic()); + + // anonymous services are shared when using decoration definitions + $container->compile(); + $services = $container->getDefinitions(); + $fooArgs = $services['foo']->getArguments(); + $barArgs = $services['bar']->getArguments(); + $this->assertSame($fooArgs[0], $barArgs[0]); } /** diff --git a/src/Symfony/Component/DependencyInjection/Variable.php b/src/Symfony/Component/DependencyInjection/Variable.php index e50235607ec8f..ddd43743826f6 100644 --- a/src/Symfony/Component/DependencyInjection/Variable.php +++ b/src/Symfony/Component/DependencyInjection/Variable.php @@ -29,8 +29,6 @@ class Variable private $name; /** - * Constructor. - * * @param string $name */ public function __construct($name) diff --git a/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php b/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php index 3e05015873588..c429417c25f80 100644 --- a/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php +++ b/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php @@ -263,7 +263,8 @@ private function buildOptionValue(\DOMElement $node) { $option = array(); - $defaultValue = (isset($node->nodeValue) && !empty($node->nodeValue)) ? $node->nodeValue : 'on'; + $defaultDefaultValue = 'select' === $this->node->nodeName ? '' : 'on'; + $defaultValue = (isset($node->nodeValue) && !empty($node->nodeValue)) ? $node->nodeValue : $defaultDefaultValue; $option['value'] = $node->hasAttribute('value') ? $node->getAttribute('value') : $defaultValue; $option['disabled'] = $node->hasAttribute('disabled'); diff --git a/src/Symfony/Component/DomCrawler/Tests/Field/ChoiceFormFieldTest.php b/src/Symfony/Component/DomCrawler/Tests/Field/ChoiceFormFieldTest.php index 8b35cc9ac5ba0..9592286305651 100644 --- a/src/Symfony/Component/DomCrawler/Tests/Field/ChoiceFormFieldTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/Field/ChoiceFormFieldTest.php @@ -351,6 +351,14 @@ public function testDisableValidation() $this->assertEquals(array('foobar'), $field->getValue(), '->disableValidation() allows to set a value which is not in the selected options.'); } + public function testSelectWithEmptyValue() + { + $node = $this->createSelectNodeWithEmptyOption(array('' => true, 'Female' => false, 'Male' => false)); + $field = new ChoiceFormField($node); + + $this->assertSame('', $field->getValue()); + } + protected function createSelectNode($options, $attributes = array(), $selectedAttrText = 'selected') { $document = new \DOMDocument(); diff --git a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php index 75ba2a760f45a..e729eeb103715 100644 --- a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php +++ b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php @@ -124,6 +124,10 @@ public function dispatch($eventName, Event $event = null) $event = new Event(); } + if (null !== $this->logger && $event->isPropagationStopped()) { + $this->logger->debug(sprintf('The "%s" event is already stopped. No listeners have been called.', $eventName)); + } + $this->preProcess($eventName); $this->preDispatch($eventName, $event); diff --git a/src/Symfony/Component/EventDispatcher/EventDispatcher.php b/src/Symfony/Component/EventDispatcher/EventDispatcher.php index 65c5cb6f6ee89..d4c5cdf58c652 100644 --- a/src/Symfony/Component/EventDispatcher/EventDispatcher.php +++ b/src/Symfony/Component/EventDispatcher/EventDispatcher.php @@ -178,10 +178,10 @@ public function removeSubscriber(EventSubscriberInterface $subscriber) protected function doDispatch($listeners, $eventName, Event $event) { foreach ($listeners as $listener) { - call_user_func($listener, $event, $eventName, $this); if ($event->isPropagationStopped()) { break; } + call_user_func($listener, $event, $eventName, $this); } } diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php index 8e12aef07b4a3..f7e3b96b462cc 100644 --- a/src/Symfony/Component/Filesystem/Filesystem.php +++ b/src/Symfony/Component/Filesystem/Filesystem.php @@ -155,24 +155,27 @@ public function touch($files, $time = null, $atime = null) */ public function remove($files) { - $files = iterator_to_array($this->toIterator($files)); + if ($files instanceof \Traversable) { + $files = iterator_to_array($files, false); + } elseif (!is_array($files)) { + $files = array($files); + } $files = array_reverse($files); foreach ($files as $file) { - if (@(unlink($file) || rmdir($file))) { - continue; - } if (is_link($file)) { // See https://bugs.php.net/52176 - $error = error_get_last(); - throw new IOException(sprintf('Failed to remove symlink "%s": %s.', $file, $error['message'])); + if (!@(unlink($file) || '\\' !== DIRECTORY_SEPARATOR || rmdir($file)) && file_exists($file)) { + $error = error_get_last(); + throw new IOException(sprintf('Failed to remove symlink "%s": %s.', $file, $error['message'])); + } } elseif (is_dir($file)) { - $this->remove(new \FilesystemIterator($file)); + $this->remove(new \FilesystemIterator($file, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS)); - if (!@rmdir($file)) { + if (!@rmdir($file) && file_exists($file)) { $error = error_get_last(); throw new IOException(sprintf('Failed to remove directory "%s": %s.', $file, $error['message'])); } - } elseif (file_exists($file)) { + } elseif (!@unlink($file) && file_exists($file)) { $error = error_get_last(); throw new IOException(sprintf('Failed to remove file "%s": %s.', $file, $error['message'])); } diff --git a/src/Symfony/Component/Form/Extension/Core/DataMapper/CheckboxListMapper.php b/src/Symfony/Component/Form/Extension/Core/DataMapper/CheckboxListMapper.php index b1169ceafa8c3..b85fb0025fe61 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataMapper/CheckboxListMapper.php +++ b/src/Symfony/Component/Form/Extension/Core/DataMapper/CheckboxListMapper.php @@ -11,9 +11,8 @@ namespace Symfony\Component\Form\Extension\Core\DataMapper; -use Symfony\Component\Form\ChoiceList\ChoiceListInterface; use Symfony\Component\Form\DataMapperInterface; -use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Exception\UnexpectedTypeException; /** * Maps choices to/from checkbox forms. @@ -26,16 +25,6 @@ */ class CheckboxListMapper implements DataMapperInterface { - /** - * @var ChoiceListInterface - */ - private $choiceList; - - public function __construct(ChoiceListInterface $choiceList) - { - $this->choiceList = $choiceList; - } - /** * {@inheritdoc} */ @@ -46,22 +35,12 @@ public function mapDataToForms($choices, $checkboxes) } if (!is_array($choices)) { - throw new TransformationFailedException('Expected an array.'); - } - - try { - $valueMap = array_flip($this->choiceList->getValuesForChoices($choices)); - } catch (\Exception $e) { - throw new TransformationFailedException( - 'Can not read the choices from the choice list.', - $e->getCode(), - $e - ); + throw new UnexpectedTypeException($choices, 'array'); } foreach ($checkboxes as $checkbox) { $value = $checkbox->getConfig()->getOption('value'); - $checkbox->setData(isset($valueMap[$value]) ? true : false); + $checkbox->setData(in_array($value, $choices, true)); } } @@ -70,6 +49,10 @@ public function mapDataToForms($choices, $checkboxes) */ public function mapFormsToData($checkboxes, &$choices) { + if (!is_array($choices)) { + throw new UnexpectedTypeException($choices, 'array'); + } + $values = array(); foreach ($checkboxes as $checkbox) { @@ -79,14 +62,6 @@ public function mapFormsToData($checkboxes, &$choices) } } - try { - $choices = $this->choiceList->getChoicesForValues($values); - } catch (\Exception $e) { - throw new TransformationFailedException( - 'Can not read the values from the choice list.', - $e->getCode(), - $e - ); - } + $choices = $values; } } diff --git a/src/Symfony/Component/Form/Extension/Core/DataMapper/RadioListMapper.php b/src/Symfony/Component/Form/Extension/Core/DataMapper/RadioListMapper.php index d08a603b5c90e..6f757c601f9b4 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataMapper/RadioListMapper.php +++ b/src/Symfony/Component/Form/Extension/Core/DataMapper/RadioListMapper.php @@ -11,8 +11,8 @@ namespace Symfony\Component\Form\Extension\Core\DataMapper; -use Symfony\Component\Form\ChoiceList\ChoiceListInterface; use Symfony\Component\Form\DataMapperInterface; +use Symfony\Component\Form\Exception\UnexpectedTypeException; /** * Maps choices to/from radio forms. @@ -25,24 +25,18 @@ */ class RadioListMapper implements DataMapperInterface { - /** - * @var ChoiceListInterface - */ - private $choiceList; - - public function __construct(ChoiceListInterface $choiceList) - { - $this->choiceList = $choiceList; - } - /** * {@inheritdoc} */ - public function mapDataToForms($data, $radios) + public function mapDataToForms($choice, $radios) { + if (!is_string($choice)) { + throw new UnexpectedTypeException($choice, 'string'); + } + foreach ($radios as $radio) { $value = $radio->getConfig()->getOption('value'); - $radio->setData($value === $data ? true : false); + $radio->setData($choice === $value); } } @@ -51,6 +45,10 @@ public function mapDataToForms($data, $radios) */ public function mapFormsToData($radios, &$choice) { + if (null !== $choice && !is_string($choice)) { + throw new UnexpectedTypeException($choice, 'null or string'); + } + $choice = null; foreach ($radios as $radio) { @@ -59,8 +57,7 @@ public function mapFormsToData($radios, &$choice) return; } - $value = $radio->getConfig()->getOption('value'); - $choice = current($this->choiceList->getChoicesForValues(array($value))); + $choice = $radio->getConfig()->getOption('value'); return; } diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php index 3bddd608982f9..6e336d4ba2dbd 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php @@ -205,6 +205,10 @@ public function reverseTransform($value) throw new TransformationFailedException('I don\'t have a clear idea what infinity looks like'); } + if (is_int($result) && $result === (int) $float = (float) $result) { + $result = $float; + } + if (false !== $encoding = mb_detect_encoding($value, null, true)) { $length = mb_strlen($value, $encoding); $remainder = mb_substr($value, $position, $length, $encoding); diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index 34566432f1599..3080ede669cd9 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -60,9 +60,7 @@ public function __construct(ChoiceListFactoryInterface $choiceListFactory = null public function buildForm(FormBuilderInterface $builder, array $options) { if ($options['expanded']) { - $builder->setDataMapper($options['multiple'] - ? new CheckboxListMapper($options['choice_list']) - : new RadioListMapper($options['choice_list'])); + $builder->setDataMapper($options['multiple'] ? new CheckboxListMapper() : new RadioListMapper()); // Initialize all choices before doing the index check below. // This helps in cases where index checks are optimized for non @@ -133,30 +131,13 @@ public function buildForm(FormBuilderInterface $builder, array $options) $event->setData($data); }); + } - if (!$options['multiple']) { - // For radio lists, transform empty arrays to null - // This is kind of a hack necessary because the RadioListMapper - // is not invoked for forms without choices - $builder->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) { - if (array() === $event->getData()) { - $event->setData(null); - } - }); - // For radio lists, pre selection of the choice needs to pre set data - // with the string value so it can be matched in - // {@link \Symfony\Component\Form\Extension\Core\DataMapper\RadioListMapper::mapDataToForms()} - $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { - $choiceList = $event->getForm()->getConfig()->getOption('choice_list'); - $value = current($choiceList->getValuesForChoices(array($event->getData()))); - $event->setData((string) $value); - }); - } - } elseif ($options['multiple']) { - // pFad - Phonifier reborn

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

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


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy