diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000000..6eaec7c81da9a --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,13 @@ +| Q | A +| ---------------- | ----- +| Bug report? | yes/no +| Feature request? | yes/no +| BC Break report? | yes/no +| RFC? | yes/no +| Symfony version | x.y.z + + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 99ba8e00213af..d35bbdeb69e23 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,11 +1,19 @@ | Q | A | ------------- | --- -| Branch? | "master" for new features / 2.7, 2.8 or 3.1 for fixes +| Branch? | master / 2.7, 2.8 or 3.2 | Bug fix? | yes/no -| New feature? | yes/no +| New feature? | yes/no | BC breaks? | yes/no -| Deprecations? | yes/no +| Deprecations? | yes/no | Tests pass? | yes/no -| Fixed tickets | comma-separated list of tickets fixed by the PR, if any +| Fixed tickets | #... | License | MIT -| Doc PR | reference to the documentation PR, if any +| Doc PR | symfony/symfony-docs#... + + diff --git a/.github/build-packages.php b/.github/build-packages.php index ec629998f600d..02a2239732be2 100644 --- a/.github/build-packages.php +++ b/.github/build-packages.php @@ -43,16 +43,20 @@ echo "Missing \"dev-master\" branch-alias in composer.json extra.\n"; exit(1); } - $package->version = str_replace('-dev', '.999', $package->extra->{'branch-alias'}->{'dev-master'}); + $package->version = str_replace('-dev', '.x-dev', $package->extra->{'branch-alias'}->{'dev-master'}); $package->dist['type'] = 'tar'; $package->dist['url'] = 'file://'.str_replace(DIRECTORY_SEPARATOR, '/', dirname(__DIR__))."/$dir/package.tar"; $packages[$package->name][$package->version] = $package; $versions = file_get_contents('https://packagist.org/packages/'.$package->name.'.json'); - $versions = json_decode($versions); + $versions = json_decode($versions)->package->versions; - foreach ($versions->package->versions as $v => $package) { + if ($package->version === str_replace('-dev', '.x-dev', $versions->{'dev-master'}->extra->{'branch-alias'}->{'dev-master'})) { + unset($versions->{'dev-master'}); + } + + foreach ($versions as $v => $package) { $packages[$package->name] += array($v => $package); } } diff --git a/.php_cs b/.php_cs deleted file mode 100644 index 77701dd97db39..0000000000000 --- a/.php_cs +++ /dev/null @@ -1,38 +0,0 @@ -setUsingLinter(false) - ->setUsingCache(true) - ->fixers(array( - 'long_array_syntax', - 'php_unit_construct', - 'php_unit_dedicate_assert', - )) - ->finder( - Symfony\CS\Finder\DefaultFinder::create() - ->in(__DIR__) - ->exclude(array( - // directories containing files with content that is autogenerated by `var_export`, which breaks CS in output code - 'src/Symfony/Component/DependencyInjection/Tests/Fixtures', - 'src/Symfony/Component/Routing/Tests/Fixtures/dumper', - // fixture templates - 'src/Symfony/Component/Templating/Tests/Fixtures/templates', - 'src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom', - // generated fixtures - 'src/Symfony/Component/VarDumper/Tests/Fixtures', - // resource templates - 'src/Symfony/Bundle/FrameworkBundle/Resources/views/Form', - )) - // file content autogenerated by `var_export` - ->notPath('src/Symfony/Component/Translation/Tests/fixtures/resources.php') - // autogenerated xmls - ->notPath('src/Symfony/Component/Console/Tests/Fixtures/application_1.xml') - ->notPath('src/Symfony/Component/Console/Tests/Fixtures/application_2.xml') - // yml - ->notPath('src/Symfony/Component/Yaml/Tests/Fixtures/sfTests.yml') - // test template - ->notPath('src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_name_entry_label.html.php') - // explicit heredoc test - ->notPath('src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/views/translation.html.php') - ) -; diff --git a/.php_cs.dist b/.php_cs.dist new file mode 100644 index 0000000000000..ee298bd15678e --- /dev/null +++ b/.php_cs.dist @@ -0,0 +1,43 @@ +setRules(array( + '@Symfony' => true, + '@Symfony:risky' => true, + 'array_syntax' => array('syntax' => 'long'), + 'no_unreachable_default_argument_value' => false, + 'braces' => array('allow_single_line_closure' => true), + 'heredoc_to_nowdoc' => false, + 'phpdoc_annotation_without_dot' => false, + )) + ->setRiskyAllowed(true) + ->setFinder( + PhpCsFixer\Finder::create() + ->in(__DIR__.'/src') + ->exclude(array( + // directories containing files with content that is autogenerated by `var_export`, which breaks CS in output code + 'Symfony/Component/DependencyInjection/Tests/Fixtures', + 'Symfony/Component/Routing/Tests/Fixtures/dumper', + // fixture templates + 'Symfony/Component/Templating/Tests/Fixtures/templates', + 'Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom', + // generated fixtures + 'Symfony/Component/VarDumper/Tests/Fixtures', + // resource templates + 'Symfony/Bundle/FrameworkBundle/Resources/views/Form', + )) + // file content autogenerated by `var_export` + ->notPath('Symfony/Component/Translation/Tests/fixtures/resources.php') + // autogenerated xmls + ->notPath('Symfony/Component/Console/Tests/Fixtures/application_1.xml') + ->notPath('Symfony/Component/Console/Tests/Fixtures/application_2.xml') + // yml + ->notPath('Symfony/Component/Yaml/Tests/Fixtures/sfTests.yml') + // test template + ->notPath('Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_name_entry_label.html.php') + // explicit heredoc test + ->notPath('Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/views/translation.html.php') + // purposefully invalid JSON + ->notPath('Symfony/Component/Asset/Tests/fixtures/manifest-invalid.json') + ) +; diff --git a/.travis.yml b/.travis.yml index 423d9260b6a9b..6668279015c13 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ env: matrix: include: # Use the newer stack for HHVM as HHVM does not support Precise anymore since a long time and so Precise has an outdated version - - php: hhvm-stable + - php: hhvm-3.18 sudo: required dist: trusty group: edge @@ -38,64 +38,145 @@ cache: - php-$MIN_PHP services: + - memcached - mongodb - redis-server before_install: - - stty cols 120 - - mkdir /tmp/slapd - - slapd -f src/Symfony/Component/Ldap/Tests/Fixtures/conf/slapd.conf -h ldap://localhost:3389 & - - PHP=$TRAVIS_PHP_VERSION - # Matrix lines for intermediate PHP versions are skipped for pull requests - - if [[ ! $deps && ! $PHP = ${MIN_PHP%.*} && ! $PHP = hhvm* && $TRAVIS_PULL_REQUEST != false ]]; then deps=skip; skip=1; fi - # A sigchild-enabled-PHP is used to test the Process component on the lowest PHP matrix line - - if [[ ! $deps && $PHP = ${MIN_PHP%.*} && ! -d php-$MIN_PHP/sapi ]]; then wget http://museum.php.net/php5/php-$MIN_PHP.tar.bz2 -O - | tar -xj; (cd php-$MIN_PHP; ./configure --enable-sigchild --enable-pcntl; make -j2); fi - - if [[ ! $PHP = hhvm* ]]; then INI_FILE=~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; else INI_FILE=/etc/hhvm/php.ini; fi - - if [[ ! $skip ]]; then echo date.timezone = Europe/Paris >> $INI_FILE; fi - - if [[ ! $skip ]]; then echo memory_limit = -1 >> $INI_FILE; fi - - if [[ ! $skip ]]; then echo session.gc_probability = 0 >> $INI_FILE; fi - - if [[ ! $skip ]]; then echo opcache.enable_cli = 1 >> $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.11 && echo apc.enable_cli = 1 >> $INI_FILE); fi - - if [[ ! $skip && $PHP = 7.* ]]; then (echo yes | pecl install -f apcu-5.1.6 && 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 echo extension = redis.so >> $INI_FILE; fi; - - if [[ ! $skip && ! $PHP = hhvm* ]]; then phpenv config-rm xdebug.ini || echo "xdebug not available"; fi - - if [[ ! $skip ]]; then [ -d ~/.composer ] || mkdir ~/.composer; cp .composer/* ~/.composer/; fi - - if [[ ! $skip ]]; then export PHPUNIT=$(readlink -f ./phpunit); fi - - if [[ ! $skip ]]; then ldapadd -h localhost:3389 -D cn=admin,dc=symfony,dc=com -w symfony -f src/Symfony/Component/Ldap/Tests/Fixtures/data/base.ldif; fi - - if [[ ! $skip ]]; then ldapadd -h localhost:3389 -D cn=admin,dc=symfony,dc=com -w symfony -f src/Symfony/Component/Ldap/Tests/Fixtures/data/fixtures.ldif; fi + - | + # General configuration + stty cols 120 + mkdir /tmp/slapd + slapd -f src/Symfony/Component/Ldap/Tests/Fixtures/conf/slapd.conf -h ldap://localhost:3389 & + PHP=$TRAVIS_PHP_VERSION + [ -d ~/.composer ] || mkdir ~/.composer + cp .composer/* ~/.composer/ + export PHPUNIT=$(readlink -f ./phpunit) + export PHPUNIT_X="$PHPUNIT --exclude-group tty,benchmark,intl-data" + export COMPOSER_UP='composer update --no-progress --no-suggest --ansi' + + # tfold is a helper to create folded reports + tfold () { + title=$1 + fold=$(echo $title | sed -r 's/[^-_A-Za-z\d]+/./g') + shift + echo -e "travis_fold:start:$fold\\n\\e[1;34m$title\\e[0m" + bash -xc "$*" 2>&1 && + echo -e "\\e[32mOK\\e[0m $title\\n\\ntravis_fold:end:$fold" || + ( echo -e "\\e[41mKO\\e[0m $title\\n" && exit 1 ) + } + export -f tfold + + # php.ini configuration + if [[ $PHP = hhvm* ]]; then + INI=/etc/hhvm/php.ini + else + INI=~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini + phpenv config-rm xdebug.ini || echo "xdebug not available" + fi + echo date.timezone = Europe/Paris >> $INI + echo memory_limit = -1 >> $INI + echo session.gc_probability = 0 >> $INI + echo opcache.enable_cli = 1 >> $INI + echo hhvm.jit = 0 >> $INI + echo apc.enable_cli = 1 >> $INI + echo extension = ldap.so >> $INI + echo extension = redis.so >> $INI + echo extension = memcached.so >> $INI + [[ $PHP = 5.* ]] && echo extension = mongo.so >> $INI + [[ $PHP = 5.* ]] && echo extension = memcache.so >> $INI + + # Matrix lines for intermediate PHP versions are skipped for pull requests + if [[ ! $deps && ! $PHP = ${MIN_PHP%.*} && ! $PHP = hhvm* && $TRAVIS_PULL_REQUEST != false ]]; then + deps=skip + skip=1 + else + COMPONENTS=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -printf '%h\n') + fi + + - | + # Install sigchild-enabled PHP to test the Process component on the lowest PHP matrix line + if [[ ! $deps && $PHP = ${MIN_PHP%.*} && ! -d php-$MIN_PHP/sapi ]]; then + wget http://museum.php.net/php5/php-$MIN_PHP.tar.bz2 -O - | tar -xj && + (cd php-$MIN_PHP && ./configure --enable-sigchild --enable-pcntl && make -j2) + fi + + - | + # Install extra PHP extensions + if [[ ! $skip && $PHP = 5.* ]]; then + ([[ $deps ]] || tfold ext.symfony_debug 'cd src/Symfony/Component/Debug/Resources/ext && phpize && ./configure && make && echo extension = $(pwd)/modules/symfony_debug.so >> '"$INI") && + tfold ext.apcu4 'echo yes | pecl install -f apcu-4.0.11' + elif [[ ! $skip && $PHP = 7.* ]]; then + tfold ext.apcu5 'echo yes | pecl install -f apcu-5.1.6' + fi + + - | + # Load fixtures + if [[ ! $skip ]]; then + ldapadd -h localhost:3389 -D cn=admin,dc=symfony,dc=com -w symfony -f src/Symfony/Component/Ldap/Tests/Fixtures/data/base.ldif && + ldapadd -h localhost:3389 -D cn=admin,dc=symfony,dc=com -w symfony -f src/Symfony/Component/Ldap/Tests/Fixtures/data/fixtures.ldif + fi install: - - if [[ ! $skip && $deps ]]; then cp composer.json composer.json.orig; fi - - if [[ ! $skip && $deps ]]; then echo -e '{\n"require":{'"$(grep phpunit-bridge composer.json)"'"php":"*"},"minimum-stability":"dev"}' > composer.json; 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 [[ ! $skip && $deps ]]; then php .github/build-packages.php HEAD^ $COMPONENTS; fi - - if [[ ! $skip && $deps ]]; then mv composer.json composer.json.phpunit; mv composer.json.orig composer.json; fi - - if [[ ! $skip && ! $deps ]]; then php .github/build-packages.php HEAD^ src/Symfony/Bridge/PhpUnit; 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'); 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 [[ ! $skip && $deps ]]; then export SYMFONY_DEPRECATIONS_HELPER=weak; fi - - if [[ ! $skip && $deps ]]; then mv composer.json.phpunit composer.json; fi - - if [[ ! $skip ]]; then composer update; fi - - if [[ ! $skip ]]; then COMPOSER_ROOT_VERSION= ./phpunit install; fi - - if [[ ! $skip && ! $PHP = hhvm* ]]; then php -i; else hhvm --php -r 'print_r($_SERVER);print_r(ini_get_all());'; fi + - | + # Create local composer packages for each patched components and reference them in composer.json files when cross-testing components + if [[ ! $deps ]]; then + php .github/build-packages.php HEAD^ src/Symfony/Bridge/PhpUnit + elif [[ ! $skip ]]; then + export SYMFONY_DEPRECATIONS_HELPER=weak && + cp composer.json composer.json.orig && + echo -e '{\n"require":{'"$(grep phpunit-bridge composer.json)"'"php":"*"},"minimum-stability":"dev"}' > composer.json && + php .github/build-packages.php HEAD^ $COMPONENTS && + mv composer.json composer.json.phpunit && + mv composer.json.orig composer.json + 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/.//) && + 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') + elif [[ ! $skip ]]; then + SYMFONY_VERSION=$(cat composer.json | grep '^ *"dev-master". *"[1-9]' | grep -o '[0-9.]*') + 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 + [[ $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) ]] && LEGACY=,legacy + + export COMPOSER_ROOT_VERSION=$SYMFONY_VERSION.x-dev + if [[ ! $skip && $deps ]]; then mv composer.json.phpunit composer.json; fi + + - if [[ ! $skip ]]; then $COMPOSER_UP; fi + - if [[ ! $skip ]]; then ./phpunit install; fi + - | + # phpinfo + if [[ ! $PHP = hhvm* ]]; then php -i; else hhvm --php -r 'print_r($_SERVER);print_r(ini_get_all());'; fi + + - | + run_tests () { + set -e + if [[ $skip ]]; then + echo -e "\\n\\e[1;34mIntermediate PHP version $PHP is skipped for pull requests.\\e[0m" + elif [[ $deps = high ]]; then + echo "$COMPONENTS" | parallel --gnu -j10% "tfold {} 'cd {} && $COMPOSER_UP && $PHPUNIT_X$LEGACY'" + elif [[ $deps = low ]]; then + echo "$COMPONENTS" | parallel --gnu -j10% "tfold {} 'cd {} && $COMPOSER_UP --prefer-lowest --prefer-stable && $PHPUNIT_X'" && + # Test the PhpUnit bridge on PHP 5.3, using the original phpunit script + tfold src/Symfony/Bridge/PhpUnit \ + "cd src/Symfony/Bridge/PhpUnit && wget https://phar.phpunit.de/phpunit-4.8.phar && phpenv global 5.3 && composer update --no-progress --ansi && php phpunit-4.8.phar" + elif [[ $PHP = hhvm* ]]; then + $PHPUNIT --exclude-group benchmark,intl-data + else + echo "$COMPONENTS" | parallel --gnu "tfold {} $PHPUNIT_X {}" + tfold tty-group $PHPUNIT --group tty + if [[ $PHP = ${MIN_PHP%.*} ]]; then + echo -e "1\\n0" | xargs -I{} bash -c "tfold src/Symfony/Component/Process.sigchild{} SYMFONY_DEPRECATIONS_HELPER=weak ENHANCE_SIGCHLD={} php-$MIN_PHP/sapi/cli/php .phpunit/phpunit-4.8/phpunit --colors=always src/Symfony/Component/Process/" + fi + fi + } script: - - if [[ $skip ]]; then echo -e "\\n\\e[1;34mIntermediate PHP version $PHP is skipped for pull requests.\\e[0m"; fi - - if [[ ! $deps && ! $PHP = hhvm* ]]; then echo "$COMPONENTS" | parallel --gnu '$PHPUNIT --exclude-group tty,benchmark,intl-data {}'; fi - - if [[ ! $deps && ! $PHP = hhvm* ]]; then echo -e "\\nRunning tests requiring tty"; $PHPUNIT --group tty; fi - - if [[ ! $deps && $PHP = hhvm* ]]; then $PHPUNIT --exclude-group benchmark,intl-data; fi - - if [[ ! $deps && $PHP = ${MIN_PHP%.*} ]]; then echo -e "1\\n0" | xargs -I{} sh -c 'echo "\\nPHP --enable-sigchild enhanced={}" && ENHANCE_SIGCHLD={} php-$MIN_PHP/sapi/cli/php .phpunit/phpunit-4.8/phpunit --colors=always src/Symfony/Component/Process/'; fi - - if [[ $deps = high ]]; then echo "$COMPONENTS" | parallel --gnu -j10% 'cd {}; composer update --no-progress --ansi; $PHPUNIT --exclude-group tty,benchmark,intl-data'$LEGACY; fi - - if [[ $deps = low ]]; then echo "$COMPONENTS" | parallel --gnu -j10% 'cd {}; composer update --no-progress --ansi --prefer-lowest --prefer-stable; $PHPUNIT --exclude-group tty,benchmark,intl-data'; fi - # Test the PhpUnit bridge using the original phpunit script - - if [[ $deps = low ]]; then (cd src/Symfony/Bridge/PhpUnit && phpenv global 5.3 && php --version && composer update && phpunit); fi + - (run_tests) diff --git a/CHANGELOG-3.1.md b/CHANGELOG-3.1.md index 228cbff557b8e..36efff588f4d8 100644 --- a/CHANGELOG-3.1.md +++ b/CHANGELOG-3.1.md @@ -7,6 +7,90 @@ in 3.1 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v3.1.0...v3.1.1 +* 3.1.9 (2017-01-12) + + * bug #21218 [Form] DateTimeToLocalizedStringTransformer does not use timezone when using date only (magnetik) + * bug #20605 [Ldap] Always have a valid connection when using the EntryManager (bobvandevijver) + * bug #21104 [FrameworkBundle] fix IPv6 address handling in server commands (xabbuh) + * bug #20793 [Validator] Fix caching of constraints derived from non-serializable parents (uwej711) + * bug #19586 [TwigBundle] Fix bug where namespaced paths don't take parent bundles in account (wesleylancel) + * bug #21237 [FrameworkBundle] Fix relative paths used as cache keys (nicolas-grekas) + * bug #21183 [Validator] respect groups when merging constraints (xabbuh) + * bug #21179 [TwigBundle] Fixing regression in TwigEngine exception handling (Bertalan Attila) + * bug #21220 [DI] Fix missing new line after private alias (ogizanagi) + * bug #21211 Classloader tmpname (lyrixx) + * bug #21205 [TwigBundle] fixed usage when Templating is not installed (fabpot) + * bug #21155 [Validator] Check cascasdedGroups for being countable (scaytrase) + * bug #21200 [Filesystem] Check that directory is writable after created it in dumpFile() (chalasr) + * bug #21165 [Serializer] int is valid when float is expected when deserializing JSON (dunglas) + * bug #21166 [Cache] Fix order of writes in ChainAdapter (nicolas-grekas) + * bug #21113 [FrameworkBundle][HttpKernel] Fix resources loading for bundles with custom structure (chalasr) + * bug #21084 [Yaml] handle empty lines inside unindented collection (xabbuh) + * bug #20925 [HttpFoundation] Validate/cast cookie expire time (ro0NL) + * bug #21032 [SecurityBundle] Made collection of user provider unique when injecting them to the RemberMeService (lyrixx) + * bug #21078 [Console] Escape default value when dumping help (lyrixx) + * bug #21076 [Console] OS X Can't call cli_set_process_title php without superuser (ogizanagi) + * bug #20900 [Console] Descriptors should use Helper::strlen (ogizanagi) + * bug #21025 [Cache] remove is_writable check on filesystem cache (4rthem) + * bug #21064 [Debug] Wrap call to ->log in a try catch block (lyrixx) + * bug #21010 [Debug] UndefinedMethodFatalErrorHandler - Handle anonymous classes (SpacePossum) + * bug #20991 [cache] Bump RedisAdapter default timeout to 5s (Nicofuma) + * bug #20859 Avoid warning in PHP 7.2 because of non-countable data (wouterj) + * bug #21053 [Validator] override property constraints in child class (xabbuh) + * bug #21034 [FrameworkBundle] Make TemplateController working without the Templating component (dunglas) + * bug #20970 [Console] Fix question formatting using SymfonyStyle::ask() (chalasr, ogizanagi) + * bug #20999 [HttpKernel] Continuation of #20599 for 3.1 (ro0NL) + * bug #20975 [Form] fix group sequence based validation (xabbuh) + * bug #20599 [WebProfilerBundle] Display multiple HTTP headers in WDT (ro0NL) + * bug #20799 [TwigBundle] do not try to register incomplete definitions (xabbuh) + * bug #20961 [Validator] phpize default option values (xabbuh) + * bug #20934 [FrameworkBundle] Fix PHP form templates on translatable attributes (ro0NL) + * bug #20957 [FrameworkBundle] test for the Validator component to be present (xabbuh) + * bug #20936 [DependencyInjection] Fix on-invalid attribute type in xsd (ogizanagi) + * bug #20931 [VarDumper] Fix dumping by-ref variadics (nicolas-grekas) + * bug #20734 [Security] AbstractVoter->supportsAttribute gives false positive if attribute is zero (0) (martynas-foodpanda) + * bug #14082 [config] Fix issue when key removed and left value only (zerustech) + * bug #20910 [HttpFoundation] Fix cookie to string conversion for raw cookies (ro0NL) + * bug #20847 [Console] fixed BC issue with static closures (araines) + +* 3.1.8 (2016-12-13) + + * bug #20714 [FrameworkBundle] Fix unresolved parameters from default configs in debug:config (chalasr) + * bug #20442 [FrameworkBundle] Bundle commands are not available via find() (julienfalque) + * bug #20840 [WebProfilerBundle] add dependency on Twig (xabbuh) + * bug #20828 [Validator] Fix init of YamlFileLoader::$classes for empty files (nicolas-grekas) + * bug #20745 [Validator] add class name to the cache key (Simperfit) + * bug #20530 [Serializer] Remove AbstractObjectNormalizer::isAttributeToNormalize (dunglas) + * bug #19141 Throw less misleading exception when property access not found (bramtweedegolf) + * bug #20539 Cast result to int before adding to it (alcaeus) + * bug #20831 [Twig] Fix deprecations with Twig 1.29 (nicolas-grekas) + * bug #20816 [FrameworkBundle] Removed kernel.debug from the cache pool namespace seed (Sander Toonen) + * bug #20646 Maintain the selected panel when redirecting to another profile (javiereguiluz) + * bug #20767 [Cache] Fix dumping SplDoublyLinkedList iter mode (nicolas-grekas) + * bug #20736 [Console] fixed PHP7 Errors when not using Dispatcher (keradus) + * bug #20756 [HttpKernel] Regression test for missing controller arguments (iltar) + * bug #20755 [HttpKernel] Regression test for missing controller arguments (iltar) + * bug #20732 fix the inline level for dumped multi-line strings (xabbuh) + * bug #20418 [Form][DX] FileType "multiple" fixes (yceruto) + * bug #19902 [DependencyInjection] PhpDumper.php: hasReference() shouldn't search references in lazy service. (antanas-arvasevicius) + * bug #20704 [Console] Fix wrong handling of multiline arg/opt descriptions (ogizanagi) + * bug #20712 [TwigBundle] Fix twig loader registered twice (ogizanagi) + * bug #20716 [WebProfilerBundle] Fix dump block is unfairly restrained (ogizanagi) + * bug #20671 [Config] ConfigCache::isFresh() should return false when unserialize() fails (nicolas-grekas) + * bug #20676 [ClassLoader] Use only forward slashes in generated class map (nicolas-grekas) + * bug #20664 [Validator] ensure the proper context for nested validations (xabbuh) + * bug #20661 bug #20653 [WebProfilerBundle] Profiler includes ghost panels (jzawadzki) + * bug #20374 [FrameworkBundle] Improve performance of ControllerNameParser (enumag) + * bug #20474 [Routing] Fail properly when a route parameter name cannot be used as a PCRE subpattern name (fancyweb) + * bug #20566 [DI] Initialize properties before method calls (ro0NL) + * bug #20609 [DI] Fixed custom services definition BC break introduced in ec7e70fb… (kiler129) + * bug #20598 [DI] Aliases should preserve the aliased invalid behavior (nicolas-grekas) + * bug #20600 [Process] Fix process continuing after reached timeout using getIterator() (chalasr) + * bug #20602 [HttpKernel] Revert BC breaking change of Request::isMethodSafe() (nicolas-grekas) + * bug #20499 [Doctrine][Form] support large integers (xabbuh) + * bug #20576 [Process] Do feat test before enabling TTY mode (nicolas-grekas) + * bug #20577 [FrameworkBundle] Mark cache.default_*_provider services private (nicolas-grekas) + * 3.1.7 (2016-11-21) * bug #20550 [YAML] Fix processing timestamp strings with timezone (myesain) diff --git a/CHANGELOG-3.2.md b/CHANGELOG-3.2.md index c892ebb6755b7..93dc435ade79f 100644 --- a/CHANGELOG-3.2.md +++ b/CHANGELOG-3.2.md @@ -7,6 +7,264 @@ in 3.2 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v3.2.0...v3.2.1 +* 3.2.7 (2017-04-05) + + * bug #22285 [HttpKernel] Fix forward compat with Request::setTrustedProxies() (nicolas-grekas) + * bug #22265 Allow Upper Case property names (insekticid) + * bug #22258 [DI] Autowiring and factories are incompatible with each others (nicolas-grekas) + * bug #22254 [DI] Don't use auto-registered services to populate type-candidates (nicolas-grekas) + * bug #22229 [ExpressionLanguage] Provide the expression in syntax errors (k0pernikus, stof) + * bug #22251 [PropertyInfo] Support nullable array or collection (4rthem) + * bug #22240 [DI] Fix fatal error at ContainerBuilder::compile() if config is not installed (chalasr) + * bug #22140 [Form] Improve the exceptions when trying to get the data in a PRE_SET_DATA listener and the data has not already been set (fancyweb) + * bug #22217 [Console] Fix table cell styling (ro0NL) + * bug #22194 [Console] CommandTester: disable color support detection (julienfalque) + * bug #22188 [Console] Revised exception rendering (ro0NL) + * bug #22154 [WebProfilerBundle] Normalize whitespace in exceptions passed in headers (curry684) + * bug #22183 [Process] Fix bug which wiped or mangled env vars (pjcdawkins) + * bug #22142 [Console] Escape exception messages in renderException (chalasr) + * bug #22172 Fix port usage in server:status command (alcaeus) + * bug #22164 [Bridge\Doctrine] Fix change breaking doctrine-bundle test suite (nicolas-grekas) + * bug #22159 [FrameworkBundle] Cache pool clear command requires at least 1 pool (ro0NL) + * bug #22133 [Filesystem] normalize paths before making them relative (xabbuh) + * bug #22138 [HttpFoundation][bugfix] $bags should always be initialized (MacDada) + * bug #21810 #21809 [SecurityBundle] bugfix: if security provider's name contains upper cases then container didn't compile (Antanas Arvasevicius) + * bug #22123 [WebProfilerBundle] Fix for CSS attribute at Profiler Translation Page (e-moe) + * bug #19778 [Security] Fixed roles serialization on token from user object (eko) + * bug #22036 Set Date header in Response constructor already (mpdude) + * bug #22022 [Validator] fix URL validator to detect non supported chars according to RFC 3986 (e-moe) + * bug #21849 [HttpFoundation] Fix missing handling of for/host/proto info from "Forwarded" header (nicolas-grekas) + * bug #21968 Fixed pathinfo calculation for requests starting with a question mark. (syzygymsu) + * bug #22027 Revert "bug #21841 [Console] Do not squash input changes made from console.command event (chalasr)" (chalasr) + * bug #21846 [HttpFoundation] Fix Request::getHost() when having several hosts in X_FORWARDED_HOST (nicolas-grekas) + * bug #21208 [Validator] Add object handling of invalid constraints in Composite (SenseException) + * bug #22044 [Serializer] [XML] Ignore Process Instruction (jordscream) + * bug #22090 [WebProfilerBundle] Fix Content-Security-Policy compatibility in case of a `style-src 'self'` policy (romainneutron) + * bug #22079 [HttpKernel] Fixed bug with purging of HTTPS URLs (ausi) + * bug #22045 [WebProfilerBundle] Handle Content-Security-Policy-Report-Only header correctly (romainneutron) + * bug #21523 #20411 fix Yaml parsing for very long quoted strings (RichardBradley) + * bug #22001 [Doctrine Bridge] fix priority for doctrine event listeners (dmaicher) + * bug #22040 [FrameworkBundle] improve message when workflows are missing (xabbuh) + * bug #22032 [FrameworkBundle] Fix translation dep constraint (chalasr) + * bug #21996 [Cache] Enhance error reporting for FilesystemAdapter (nicolas-grekas) + * bug #21981 [Console] Use proper line endings in BufferedOutput (julienfalque) + * bug #21976 [VarDumper] Add missing isset() checks in some casters (nicolas-grekas) + * bug #21973 [VarDumper] Add missing isset() checks in some casters (nicolas-grekas) + * bug #21957 [Form] Choice type int values (BC Fix) (mcfedr) + +* 3.2.6 (2017-03-10) + + * bug #21930 [Cache] Cached files rely on umask (4rthem) + * bug #21946 Use PHPUnit 5.4 instead of 5.3 (j0k3r) + * bug #21936 [PropertyAccess] Use ArrayAdapter in debug mode (chalasr) + +* 3.2.5 (2017-03-09) + + * bug #21923 [travis] Test with hhvm 3.18 (nicolas-grekas) + * bug #21793 [Workflow] Fixed marking state on leave and enter events (HeahDude) + * bug #21912 [Yaml] dump escape sequences when possible (xabbuh) + * bug #21908 [Cache] Fix Redis pipelining/multi-ops (nicolas-grekas) + * bug #21823 dumpFile(), preserve existing file permissions (chs2) + * bug #21880 [Form] Fixed overridden choices option in extended choice types (HeahDude) + * bug #21896 [PHPunitBridge] Count @expectedDeprecation as an assertion (wouterj) + * bug #21865 [Security] context listener: hardening user provider handling (xabbuh) + * bug #21883 [HttpKernel] fix Kernel name when stored in a directory starting with a number (fabpot) + * bug #21841 [Console] Do not squash input changes made from console.command event (chalasr) + * bug #21481 [Form] Fixed empty conversion of Intl types (HeahDude) + * bug #21671 [Serializer] Xml encoder throws exception for valid data (gr1ev0us) + * bug #21805 Provide less state in getRequestFormat (dawehner) + * bug #21851 Adding use statement for InvalidArgumentException (Nyholm) + * bug #21832 [Routing] Ignore hidden directories when loading routes from annotations (jakzal) + * bug #21769 [Form] Improve rounding precision (foaly-nr1) + * bug #21825 [PhpUnitBridge] disable global test listener when not registered (xabbuh) + * bug #21267 [Form] Fix ChoiceType to ensure submitted data is not nested unnecessarily (issei-m) + * bug #21813 Update phpstorm helper to the official format (pierredup) + * bug #21731 Fix emacs link (rubenrua) + * bug #21802 Fix issues reported by static analyse (romainneutron) + * bug #21800 Fix issues reported by static analyze (romainneutron) + * bug #21782 [DependencyInjection] add missing dumped private services list in a container frozen constructor. (hhamon) + * bug #21798 Revert "bug #21791 [SecurityBundle] only pass relevant user provider (xabbuh)" (xabbuh) + * bug #21791 [SecurityBundle] only pass relevant user provider (xabbuh) + * bug #21776 [Process] Fix ignoring of bad env var names (nicolas-grekas) + * bug #21787 [PhpUnitBridge] do not register the test listener twice (xabbuh) + * bug #21756 [Yaml] Stop replacing NULLs when merging (gadelat) + * bug #21689 [WebServerBundle] fixed html attribute escape (Seb33300) + * bug #21722 [ExpressionLanguage] Registering functions after calling evaluate(), compile() or parse() is not supported (maidmaid) + * bug #21679 [SecurityBundle] fix priority ordering of security voters (xabbuh) + * bug #21656 [DoctrineBridge] Fixed validating custom doctrine type columns (dmaicher) + * bug #21115 [Validator] do not guess getter method names (xabbuh) + * bug #21670 [DependencyInjection] Fix autowiring types when there are more than 2 services colliding (GuilhemN) + * bug #21665 [DependencyInjection] Fix autowiring collisions detection (nicolas-grekas, GuilhemN) + * bug #21661 Fix Composer constraints (fabpot) + * bug #21582 [HttpCache] purge both http and https from http cache (dbu) + * bug #21637 [FrameworkBundle] remove translation data collector when not usable (xabbuh) + * bug #21647 [Yaml] consistently parse omitted keys as the colon (xabbuh) + +* 3.2.4 (2017-02-16) + + * bug #21634 [VarDumper] Added missing persistent stream cast (lyrixx) + * bug #21436 [DependencyInjection] check for circular refs caused by method calls (xabbuh) + * bug #21400 [Serializer] fix upper camel case conversion (see #21399) (markusu49) + * bug #21599 [Console][Table] fixed render when using multiple rowspans. (aitboudad) + * bug #21613 [Process] Permit empty suffix on Windows (Bilge) + * bug #21057 [DI] Auto register extension configuration classes as a resource (ro0NL) + * bug #21607 Improve tracking of environment variables in the case of private services (tgalopin) + * bug #21592 [Validator] property constraints can be added in child classes (angelk, xabbuh) + * bug #21458 [Config] Early return for DirectoryResource (robfrawley) + * bug #21562 [DoctrineBridge] make sure that null can be the invalid value (xabbuh) + * bug #21556 [FrameworkBundle] Wire ArrayCache for annotation reader at bootstrap (nicolas-grekas) + * bug #21584 [WebProfilerBundle] Readd Symfony version status in the toolbar (wouterj) + * bug #21557 [VarDumper] Improve dump of AMQP* Object (lyrixx) + * bug #21579 [Security] LdapUserProvider should not throw an exception if the UID key does not exist in an LDAP entry (csarrazi) + * bug #21552 [FrameworkBundle] Fix annotations cache folder path (akeeman) + * bug #21542 [VarDumper] Fixed dumping of terminated generator (lyrixx) + * bug #21292 Ignore missing 'debug.file_link_formatter' service in Debug bundle (core23) + +* 3.2.3 (2017-02-06) + + * bug #21528 [Cache] Fix class exists checks in PhpArrayAdapter (nicolas-grekas) + * bug #20844 [Config] Fix checking cache for non existing meta file (hason) + * bug #21063 [Form] Fixed DateType format option for single text widget (HeahDude) + * bug #21430 Casting TableCell value to string. (jaydiablo) + * bug #21359 [FrameworkBundle] fixed custom domain for translations in php templates (robinlehrmann) + * bug #21485 [Process] Non ASCII characters disappearing during the escapeshellarg (GuillaumeVerdon) + * bug #21370 [FrameworkBundle] Execute the PhpDocExtractor earlier (GuilhemN) + * bug #21462 [BrowserKit] ignore invalid cookies expires date format (xabbuh) + * bug #21438 [Console] Fix TableCell issues with decoration (ogizanagi) + * bug #21431 [DoctrineBridge] always check for all fields to be mapped (xabbuh) + * bug #21360 [PropertyAccess] Handle interfaces in the invalid argument exception (fancyweb) + * bug #21403 [DI] Fix defaults overriding empty strings in AutowirePass (nicolas-grekas) + * bug #21401 [Debug] Workaround "null" $context (nicolas-grekas) + * bug #21381 [FrameworkBundle] Dont wire "annotations.cached_reader" before removing passes (nicolas-grekas) + * bug #21387 Fix double escaping of the decision attributes in the profiler (stof) + * bug #21372 [DependencyInjection] Fixed variadic method parameter in autowired classes (brainexe) + * bug #21338 [Cache] Fix tags expiration (nicolas-grekas) + * bug #21333 [HttpKernel] Fix ArgumentValueResolver for arguments default null (chalasr) + * bug #20871 [HttpKernel] Give higher priority to adding request formats (akeeman) + * bug #21332 [PropertyInfo] Don't try to access a property thru a static method (dunglas) + * bug #21336 [PhpUnit] Blacklist DeprecationErrorHandler in stack traces (nicolas-grekas) + * bug #21331 [PropertyInfo] Exclude static methods form properties guessing (dunglas) + * bug #21280 [Workflow] Fixed support of multiple transitions with the same name. (lyrixx) + * bug #21271 [Workflow] Added new validator to make sure each place has unique translation names (Nyholm) + * bug #21323 [Cache] [PdoAdapter] Fix MySQL 1170 error (blob as primary key) (akeeman) + * bug #21318 Don't add csp-headers if none are required (arjenm) + * bug #21291 [Ldap] Ldap username case fix (quentinus95) + * bug #21311 [Debug] Fix fatal error when changing ErrorHandler loggers if an exception is buffered (skalpa) + * bug #21288 [Doctrine Bridge] fix UniqueEntityValidator for composite object primary keys (dmaicher, HeahDude) + * bug #21285 [TwigBundle] do not lose already set method calls (xabbuh) + * bug #21279 #20411 fix Yaml parsing for very long quoted strings (RichardBradley) + * bug #21276 [Cache] Fix missing use statement in FilesystemAdapter (Lctrs) + * bug #21269 [Cache] Using strpbrk() instead of strcspn() is faster (nicolas-grekas) + +* 3.2.2 (2017-01-12) + + * bug #21257 [Profiler][Form] Fix form profiler errors profiler_dump (ogizanagi) + * bug #21243 [FrameworkBundle] Fix class_exists() checks in PhpArrayAdapter-related cache warmers (nicolas-grekas, mpajunen) + * bug #21218 [Form] DateTimeToLocalizedStringTransformer does not use timezone when using date only (magnetik) + * bug #20605 [Ldap] Always have a valid connection when using the EntryManager (bobvandevijver) + * bug #21104 [FrameworkBundle] fix IPv6 address handling in server commands (xabbuh) + * bug #20793 [Validator] Fix caching of constraints derived from non-serializable parents (uwej711) + * bug #19586 [TwigBundle] Fix bug where namespaced paths don't take parent bundles in account (wesleylancel) + * bug #21237 [FrameworkBundle] Fix relative paths used as cache keys (nicolas-grekas) + * bug #21183 [Validator] respect groups when merging constraints (xabbuh) + * bug #21179 [TwigBundle] Fixing regression in TwigEngine exception handling (Bertalan Attila) + * bug #21220 [DI] Fix missing new line after private alias (ogizanagi) + * bug #21211 Classloader tmpname (lyrixx) + * bug #21205 [TwigBundle] fixed usage when Templating is not installed (fabpot) + * bug #21155 [Validator] Check cascasdedGroups for being countable (scaytrase) + * bug #21200 [Filesystem] Check that directory is writable after created it in dumpFile() (chalasr) + * bug #21186 [Bridge/PhpUnit] Relax expectedDeprecation for forward compat (nicolas-grekas) + * bug #21184 [FrameworkBundle] Remove Response* from classes to compile (nicolas-grekas) + * bug #21165 [Serializer] int is valid when float is expected when deserializing JSON (dunglas) + * bug #21167 [Cache] Remove silenced warning tiggered by PhpArrayAdapter (nicolas-grekas) + * bug #21166 [Cache] Fix order of writes in ChainAdapter (nicolas-grekas) + * bug #21113 [FrameworkBundle][HttpKernel] Fix resources loading for bundles with custom structure (chalasr) + * bug #20995 [DependencyInjection] Fix the priority order of compiler pass trait (francoispluchino) + * bug #21084 [Yaml] handle empty lines inside unindented collection (xabbuh) + * bug #21143 [PhpUnitBridge] Set COMPOSER_ROOT_VERSION while installing (nicolas-grekas) + * bug #20925 [HttpFoundation] Validate/cast cookie expire time (ro0NL) + * bug #21138 [PhpUnitBridge] skip tests with failure and error states too (xabbuh) + * bug #21135 [PhpUnitBridge] hide stack trace of expected deprecation failures (xabbuh) + * bug #21117 [Yaml] add missing indicator character (xabbuh) + * bug #21121 [PhpUnitBridge] respect skipped and incomplete tests (xabbuh) + * bug #21032 [SecurityBundle] Made collection of user provider unique when injecting them to the RemberMeService (lyrixx) + * bug #21078 [Console] Escape default value when dumping help (lyrixx) + * bug #21076 [Console] OS X Can't call cli_set_process_title php without superuser (ogizanagi) + * bug #20900 [Console] Descriptors should use Helper::strlen (ogizanagi) + * bug #21025 [Cache] remove is_writable check on filesystem cache (4rthem) + * bug #21064 [Debug] Wrap call to ->log in a try catch block (lyrixx) + * bug #21069 [Debug] Fixed cast of stream (lyrixx) + * bug #21010 [Debug] UndefinedMethodFatalErrorHandler - Handle anonymous classes (SpacePossum) + * bug #20991 [cache] Bump RedisAdapter default timeout to 5s (Nicofuma) + * bug #20959 [FrameworkBundle] Ignore AnnotationException exceptions in the AnnotationsCacheWarmer (fancyweb) + * bug #20795 [FrameworkBundle] Allow multiple transitions with the same name (Padam87) + * bug #20859 Avoid warning in PHP 7.2 because of non-countable data (wouterj) + * bug #21053 [Validator] override property constraints in child class (xabbuh) + * bug #21034 [FrameworkBundle] Make TemplateController working without the Templating component (dunglas) + * bug #20970 [Console] Fix question formatting using SymfonyStyle::ask() (chalasr, ogizanagi) + * bug #20999 [HttpKernel] Continuation of #20599 for 3.1 (ro0NL) + * bug #20975 [Form] fix group sequence based validation (xabbuh) + * bug #20599 [WebProfilerBundle] Display multiple HTTP headers in WDT (ro0NL) + * bug #20799 [TwigBundle] do not try to register incomplete definitions (xabbuh) + * bug #20961 [Validator] phpize default option values (xabbuh) + * bug #20934 [FrameworkBundle] Fix PHP form templates on translatable attributes (ro0NL) + * bug #20957 [FrameworkBundle] test for the Validator component to be present (xabbuh) + * bug #20936 [DependencyInjection] Fix on-invalid attribute type in xsd (ogizanagi) + * bug #20931 [VarDumper] Fix dumping by-ref variadics (nicolas-grekas) + * bug #20749 [FrameworkBundle] Smarter default for framework.annotations (ogizanagi) + * bug #20734 [Security] AbstractVoter->supportsAttribute gives false positive if attribute is zero (0) (martynas-foodpanda) + * bug #14082 [config] Fix issue when key removed and left value only (zerustech) + * bug #20910 [HttpFoundation] Fix cookie to string conversion for raw cookies (ro0NL) + * bug #20909 Fix misresolved parameters in debug:config on 3.2 (chalasr) + * bug #20904 [TwigBundle] Config is now a hard dependency (dunglas) + * bug #20847 [Console] fixed BC issue with static closures (araines) + +* 3.2.1 (2016-12-13) + + * bug #20891 Add support for REDIS_URL environment variables. (robinvdvleuten) + * bug #20724 [WebProfilerBundle] Fix AJAX panel with fetch requests (OnekO) + * bug #20883 Don’t compile when Opcache is not enabled on CLI (ruudk) + * bug #20877 DateIntervalType: 'invert' should not inherit the 'required' option (galeaspablo) + * bug #20886 [Form] DateIntervalType: Do not try to translate choices (ogizanagi) + * bug #20855 [Yaml] do not trigger deprecations for valid YAML (xabbuh) + * bug #20714 [FrameworkBundle] Fix unresolved parameters from default configs in debug:config (chalasr) + * bug #20862 Allow simple-phpunit to be used with an HTTP proxy (Cydonia7) + * bug #20882 [TwigBridge] fix constructor args check (xabbuh) + * bug #20860 [WebProfilerBundle] Fix a web profiler form issue with fields added to the form after the form was built (tgalopin) + * bug #20442 [FrameworkBundle] Bundle commands are not available via find() (julienfalque) + * bug #20840 [WebProfilerBundle] add dependency on Twig (xabbuh) + * bug #20833 [HttpKernel] Fix open_basedir compat in DataCollector (nicolas-grekas) + * bug #20828 [Validator] Fix init of YamlFileLoader::$classes for empty files (nicolas-grekas) + * bug #20688 [FrameworkBundle] Resolve env params in debug:config command (nicolas-grekas) + * bug #20725 [HttpKernel] Fix annotation cache warmer with failing or missing classes (nicolas-grekas) + * bug #20830 [FrameworkBundle] Fix validation cache warmer with failing or missing classes (nicolas-grekas) + * bug #20760 [FrameworkBundle] [Workflow] Fix service marking store configuration (fduch) + * bug #20745 [Validator] add class name to the cache key (Simperfit) + * bug #20530 [Serializer] Remove AbstractObjectNormalizer::isAttributeToNormalize (dunglas) + * bug #19141 Throw less misleading exception when property access not found (bramtweedegolf) + * bug #20539 Cast result to int before adding to it (alcaeus) + * bug #20831 [Twig] Fix deprecations with Twig 1.29 (nicolas-grekas) + * bug #20701 Ignore missing 'debug.file_link_formatter' service in Debug and Twig bundles (mbabker) + * bug #20816 [FrameworkBundle] Removed kernel.debug from the cache pool namespace seed (Sander Toonen) + * bug #20769 [Bridge\Twig] Trigger deprecation when using FormExtension::$renderer (nicolas-grekas) + * bug #20646 Maintain the selected panel when redirecting to another profile (javiereguiluz) + * bug #20767 [Cache] Fix dumping SplDoublyLinkedList iter mode (nicolas-grekas) + * bug #20690 [Serializer] Fix argument object denormalization (ogizanagi) + * bug #20762 [Form] Fix FormDataCollector (nicolas-grekas, Padam87) + * bug #20747 [HttpKernel] Fixed RequestDataCollector handling of null header values. (Gabriel Moreira) + * bug #20727 [TwigBundle] Inject project root path into twig filesystem loader (4rthem) + * bug #20736 [Console] fixed PHP7 Errors when not using Dispatcher (keradus) + * bug #20756 [HttpKernel] Regression test for missing controller arguments (iltar) + * bug #20755 [HttpKernel] Regression test for missing controller arguments (iltar) + * bug #20732 fix the inline level for dumped multi-line strings (xabbuh) + * bug #20418 [Form][DX] FileType "multiple" fixes (yceruto) + * bug #19902 [DependencyInjection] PhpDumper.php: hasReference() shouldn't search references in lazy service. (antanas-arvasevicius) + * bug #20704 [Console] Fix wrong handling of multiline arg/opt descriptions (ogizanagi) + * bug #20700 [WebProfilerBundle][Translator] Fix TranslationDataCollector should use cloneVar (ogizanagi) + * bug #20712 [TwigBundle] Fix twig loader registered twice (ogizanagi) + * bug #20716 [WebProfilerBundle] Fix dump block is unfairly restrained (ogizanagi) + * bug #20717 Fix hide button in toolbar (nicolasdewez) + * 3.2.0 (2016-11-30) * bug #20687 [FrameworkBundle] Forbid env parameters in routing configuration (nicolas-grekas) diff --git a/CHANGELOG-3.3.md b/CHANGELOG-3.3.md new file mode 100644 index 0000000000000..424414f020a2e --- /dev/null +++ b/CHANGELOG-3.3.md @@ -0,0 +1,264 @@ +CHANGELOG for 3.3.x +=================== + +This changelog references the relevant changes (bug and security fixes) done +in 3.3 minor versions. + +To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash +To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v3.3.0...v3.3.1 + +* 3.3.0-BETA1 (2017-05-01) + + * feature #22530 Making tags under _defaults always apply (weaverryan) + * feature #22590 [Lock] remove the component from 3.3 (fabpot) + * feature #22527 [DI] Throw useful exception on bad XML argument tags (nicolas-grekas) + * feature #22537 [Serializer] Allow to pass csv encoder options in context (ogizanagi) + * feature #22563 Not allowing autoconfigure, instanceofConditionals or defaults for ChildDefinition (weaverryan) + * feature #22441 [Console] Review console.ERROR related behavior (nicolas-grekas) + * feature #22234 [DI] Introducing autoconfigure: automatic _instanceof configuration (weaverryan) + * feature #21502 Persist app bootstrapping logs for logger datacollector (ScullWM, nicolas-grekas) + * feature #22459 [HttpKernel] Fix deprecation of Extension::addClassesToCompile() / AddClassesToCachePass (nicolas-grekas) + * feature #22416 [FrameworkBundle][Workflow] Deprecate the default type of a workflow (lyrixx) + * feature #22313 [Workflow] Move ValidateWorkflowsPass to the Workflow component (chalasr) + * feature #22420 [DI] Make tagged abstract services throw earlier (nicolas-grekas) + * feature #22384 [DI] Replace autowiring BC break by regular deprecation (nicolas-grekas) + * feature #22383 added a more specialized exception for a better error message (fabpot) + * feature #22356 [DI] Rework config hierarchy: defaults > instanceof > service config (weaverryan, nicolas-grekas) + * feature #22362 [DI] Populate class of ChildDefinition when its id matches an existing FQCN (nicolas-grekas) + * feature #22239 [Finder] Glob wildcard while using double-star without ending slash (sroze) + * feature #22273 Add a new Link component (dunglas) + * feature #22315 Add Kernel::getProjectDir() (fabpot) + * feature #22314 [HttpKernel][FrameworkBundle] Dump container logs in Kernel, to have them also on errors (nicolas-grekas) + * feature #22316 [WebServerBundle] added a way to dump current status host/port/address when getting the status (fabpot) + * feature #22323 [DI] Report cascades of autowiring error messages (nicolas-grekas) + * feature #22306 [DI] Restrict autowired registration to "same-vendor" namespaces (nicolas-grekas) + * feature #22295 [BC BREAK][DI] Always autowire "by id" instead of using reflection against all existing services (nicolas-grekas) + * feature #20951 Redesigned the exception pages (javiereguiluz) + * feature #21919 [Form] Deprecated usage of "choices" option in sub types (HeahDude) + * feature #22274 [Yaml] report deprecations when linting YAML files (xabbuh) + * feature #22286 [DI/Yaml] Remove `@experimental` flag from "instanceof" and "prototype" (nicolas-grekas) + * feature #22181 [Console] Allow to catch CommandNotFoundException (chalasr) + * feature #22296 Bump monolog to 1.19 and use the agent regex const from parent (redthor) + * feature #21437 [Security] Use IteratorArgument for voters (jvasseur) + * feature #22277 [DI] Add "factory" support to named args and autowiring (nicolas-grekas) + * feature #22276 [FrameworkBundle] Returns the kernel instance in KernelTestCase::bootKernel (lyrixx) + * feature #22256 [DI] Reduce complexity of autowiring (nicolas-grekas) + * feature #22238 [BC BREAK][HttpFoundation] Request::setTrustedProxies() takes a new required $trustedHeaderSet argument (nicolas-grekas) + * feature #22175 [DI] add ServiceLocatorTagPass::register() to share service locators (nicolas-grekas) + * feature #22180 [Workflow] Added 'workflow_marked_places' twig function (lyrixx) + * feature #22185 [DI] Enhance DX by throwing instead of triggering a deprecation notice (nicolas-grekas) + * feature #22060 [DI] Add "by-id" autowiring: a side-effect free variant of it based on the class<>id convention (nicolas-grekas) + * feature #22158 Revert "feature #20973 [DI] Add getter injection (nicolas-grekas)" (nicolas-grekas) + * feature #22157 [FrameworkBundle] Introduce AbstractController, replacing ControllerTrait (nicolas-grekas) + * feature #22046 [Asset] Adding a new version strategy that reads from a manifest JSON file (weaverryan) + * feature #22129 [WebProfilerBundle] Improve cache panel (ro0NL) + * feature #21819 [Twig Bridge] A simpler way to retrieve flash messages (javiereguiluz) + * feature #19026 [Security] Strengthen comparison of target_url vs login_path (mrzard) + * feature #19496 [DX][Form][Validator] Add ability check if cocrete constraint fails. (Koc) + * feature #18140 [Console] Add console.ERROR event and deprecate console.EXCEPTION (wouterj) + * feature #22120 [FrameworkBundle] Multiple services on one Command class (SenseException) + * feature #21771 [FrameworkBundle] Add new "controller.service_arguments" tag to inject services into actions (nicolas-grekas) + * feature #22114 [lock] Rename Quorum into Strategy (jderusse) + * feature #20516 [Security][SecurityBundle] Enhance automatic logout url generation (ogizanagi) + * feature #22081 [FrameworkBundle][Validator] Move Validator passes to the component (chalasr) + * feature #20567 [WebProfilerBundle] Improved cookie traffic (ro0NL) + * feature #19887 Sort alternatives alphabetically when a command is not found (javiereguiluz) + * feature #20851 [Cache] Add CacheItem::getPreviousTags() (nicolas-grekas) + * feature #21830 [HttpFoundation] Add $trustedHeaderSet arg to Request::setTrustedProxies() - deprecate not setting it (nicolas-grekas) + * feature #21924 [FrameworkBundle] Allow to configure Serializer mapping paths (chalasr) + * feature #19278 [FrameworkBundle] Added about command (ro0NL) + * feature #21708 [DI] Add and wire ServiceSubscriberInterface - aka explicit service locators (nicolas-grekas) + * feature #22011 [FrameworkBundle][Serializer] Add option to register a circular_reference_handler (lyrixx) + * feature #19673 [DI] Deprecate Container::isFrozen and introduce isCompiled (ro0NL) + * feature #19954 [Console] Exclude empty namespaces in text descriptor (ro0NL) + * feature #21093 [Lock] Create a lock component (jderusse) + * feature #21007 [WebProfilerBundle] Improve AJAX toolbar panel (ro0NL) + * feature #20642 [FrameworkBundle] Add project directory default for installing assets (Noah Heck) + * feature #20365 [TwigBridge] Handle form label attributes like others (ro0NL) + * feature #22010 [FrameworkBundle][Translator] Make the Translator works with any PSR-11 container (chalasr) + * feature #21038 [FrameworkBundle] deprecated cache:clear with warmup (fabpot) + * feature #22098 [*Bundle] Add autowiring aliases for common services (nicolas-grekas) + * feature #22095 [DI] Add logging and better failure recovery to AutowirePass (nicolas-grekas) + * feature #21889 Deprecate the special SYMFONY__ environment variables (javiereguiluz) + * feature #22059 [Yaml] deprecate "? " starting unquoted strings (xabbuh) + * feature #22030 [DI] Remove skipping magic for autowired methods (nicolas-grekas) + * feature #22024 [DI] Introduce "container.service_locator" tag, replaces ServiceLocatorArgument (nicolas-grekas) + * feature #21837 [FrameworkBundle] Lazy configuration of annotations' loader and `@required` (nicolas-grekas) + * feature #21970 [DependencyInjection] Support anonymous services in Yaml (GuilhemN) + * feature #21979 [FrameworkBundle][TwigBundle] Require PSR-11 container instead of Symfony container (enumag) + * feature #21935 [FrameworkBundle][Workflow] Add a way to register a guard expression in the configuration (lyrixx) + * feature #21080 [FrameworkBundle][Monolog] Added a new way to follow logs (lyrixx) + * feature #21978 [DoctrineBridge][Routing] Require PSR-11 container instead of Symfony container (enumag) + * feature #21950 [Workflow] Added fluent interface to the DefinitionBuilder (lyrixx) + * feature #21933 [FrameworkBundle][Workflow] Add a way to enable the AuditTrail Logger (lyrixx) + * feature #21925 [Workflow] Added the workflow name to all events dispatched (lyrixx) + * feature #21774 [Yaml] deprecate implicit string casting of mapping keys (xabbuh) + * feature #21780 [DX] [Form] Add helper method to register form extensions during unit testing (pierredup) + * feature #21842 [HttpKernel] Allow signing URIs with a custom query string parameter (thewilkybarkid) + * feature #21705 [Bridge/Monolog] Enhance the Console Handler (lyrixx) + * feature #21893 Added a castToArray() config helper (javiereguiluz) + * feature #21421 Use proper error message when session write fails #20807 (digilist) + * feature #21770 [DI] Allow extensions to create ServiceLocator as services (nicolas-grekas) + * feature #21767 [DI][Router][DX] Invalidate routing cache when container parameters changed (ogizanagi) + * feature #21835 [FrameworkBundle][Routing] Move RoutingResolverPass to the Routing component (chalasr) + * feature #21815 [FrameworkBundle][HttpKernel] Move ControllerArgumentValueResolverPass to the HttpKernel component (chalasr) + * feature #21824 Add deprecation note on routing class parameters (lepiaf) + * feature #21854 [Router] Follow symlinks and skip dots in the annotation directory loader (jakzal) + * feature #18193 [FrameworkBundle] Introduce autowirable ControllerTrait (dunglas) + * feature #20680 DoctrineDataCollector: taught sanitizeParam to support classes with __toString implemented. (FractalizeR) + * feature #21828 [PhpUnitBridge] include expected deprecations in assertion counter (xabbuh) + * feature #21763 [DI] Replace wildcard-based methods autowiring by `@required` annotation (nicolas-grekas) + * feature #21730 [DependencyInjection] Use a service locator in AddConstraintValidatorsPass (GuilhemN) + * feature #21118 [Yaml] parse omitted inlined mapping values as null (xabbuh) + * feature #21806 [FrameworkBundle][PropertyInfo] Move PropertyInfoPass to the PropertyInfo component (chalasr) + * feature #19822 [HttpKernel] Deprecate X-Status-Code for better alternative (jameshalsall) + * feature #21228 [Console] Explicitly passed options without value (or empty) should remain empty (chalasr) + * feature #21723 [Routing][DX] Add full route definition for invokable controller/class (yceruto) + * feature #21768 [HttpKernel] Add a ContainerControllerResolver (psr-11) (ogizanagi) + * feature #21690 [Form] allow form types + form type extensions + form type guessers to be private services (hhamon) + * feature #21755 [Routing] Optimised dumped router matcher, prevent unneeded function calls. (frankdejonge) + * feature #21375 [FrameworkBundle][Config] Move ConfigCachePass from FrameworkBundle to Config (Deamon) + * feature #21786 [PhpUnitBridge] testing for deprecations is not risky (xabbuh) + * feature #21792 [Security] deprecate multiple providers in context listener (xabbuh) + * feature #21625 Remove some container injections in favor of service locators (nicolas-grekas, chalasr) + * feature #21539 Introduce weak vendors mode (greg0ire) + * feature #21638 [VarDumper] Allow seamless use of Data clones (nicolas-grekas) + * feature #21164 [HttpKernel] Added the SessionValueResolver (iltar) + * feature #21718 [SecurityBundle] Don't normalize username of in-memory users (chalasr) + * feature #20107 Added a build method to the kernel to replace Bundle::build() (iltar) + * feature #21694 [Bridge/PhpUnit] Add PHPUnit 6 support (nicolas-grekas) + * feature #21122 [ExpressionLanguage] Create an ExpressionFunction from a PHP function name (maidmaid) + * feature #21653 [VarDumper] Added a way to print or not comma separator and/or trailing comma (lyrixx) + * feature #21471 [Yaml] Allow dumping empty array as YAML sequence (c960657) + * feature #21478 [Asset] Add support for preloading with links and HTTP/2 push (dunglas) + * feature #20632 [FrameworkBundle] Make use of stderr for non reliable output (chalasr, ogizanagi) + * feature #21664 [Console] simplify the implementation of SymfonyStyle::comment() (fabpot) + * feature #21578 [Translation] Added a lint:xliff command for XLIFF files (javiereguiluz) + * feature #21635 added support for glob loaders in Config (fabpot) + * feature #21654 [PropertyInfo] Use iterators for PropertyInfoExtractor (GuilhemN) + * feature #21655 [PropertyInfo] Make classes final (GuilhemN) + * feature #21530 [DependencyInjection] Add "instanceof" section for local interface-defined configs (nicolas-grekas, dunglas) + * feature #21643 [Yaml] deprecate parsing mappings without keys (xabbuh) + * feature #20677 [DX][SecurityBundle] UserPasswordEncoderCommand: ask user class choice question (ogizanagi) + * feature #21283 [Form][FrameworkBundle] Move FormPass to the Form component (chalasr) + * feature #21293 [FrameworkBundle][Serializer] Move SerializerPass to the Serializer (chalasr) + * feature #21450 [Security] Lazy load guard authenticators and authentication providers (chalasr) + * feature #21484 [DI] Deprecate underscore-services in YamlFileLoader (nicolas-grekas) + * feature #21270 [DependencyInjection] Use glob pattern to load config files (pierredup) + * feature #19815 [WebProfilerBundle] Make the IP address in the profiler header clickable (jameshalsall) + * feature #21383 [DependencyInjection] Add support for named arguments (dunglas, nicolas-grekas) + * feature #19371 [Serializer] Give access to the context to support* methods (dunglas) + * feature #21553 [DI] Replace container injection by explicit service locators (chalasr) + * feature #18834 [Serializer] Add the possibility to filter attributes (dunglas) + * feature #20787 [Workflow] Added an entered event (Padam87) + * feature #21289 [DI] Add prototype services for PSR4-based discovery and registration (nicolas-grekas) + * feature #21465 [Debug] Support `@final` on methods (GuilhemN) + * feature #21505 [Config][DI] Add ComposerResource to track the runtime engine + deps (nicolas-grekas) + * feature #21533 [DI] Deprecate (un)setting pre-defined services (ro0NL) + * feature #21194 [Yaml] Add tags support (GuilhemN) + * feature #21460 [DI] ContainerBuilder::compile() can optionally resolve env vars in parameter bag (nicolas-grekas) + * feature #21572 [Finder] Add double-star matching to Glob::toRegex() (nicolas-grekas) + * feature #21265 [DI] Implement PSR-11 (greg0ire) + * feature #21474 [Process] Accept command line arrays and per-run env vars, fixing signaling and escaping (nicolas-grekas) + * feature #21517 [FrameworkBundle] Add missing autowiring aliases for common interfaces (chalasr) + * feature #21516 [HttpKernel][FrameworkBundle] Lazy load argument value resolvers (chalasr) + * feature #21031 [DI] Getter autowiring (dunglas) + * feature #21419 [DI][Config] Add & use ReflectionClassResource (nicolas-grekas) + * feature #21455 [DI] Allow to count on lazy collection arguments (ogizanagi) + * feature #21408 [DI] Add ContainerBuilder::fileExists() for checking/tracking resource existence (chalasr) + * feature #21470 [Process] Deprecate not inheriting env vars + compat related settings (nicolas-grekas) + * feature #21494 [DI] Deprecate autowiring-types in favor of aliases (nicolas-grekas) + * feature #21451 [SecurityBundle] Lazy load request matchers in FirewallMap (chalasr) + * feature #20973 [DI] Add getter injection (nicolas-grekas) + * feature #21396 [DI] Enhance logging in compiler passes (nicolas-grekas) + * feature #21402 [Security] make LdapBindAuthenticationProvider capable of searching for the DN (lsmith77, nietonfir) + * feature #21404 [DI] Generalize constructor autowiring to partial method calls (nicolas-grekas) + * feature #21388 [Debug] Deprecate ContextErrorException (nicolas-grekas) + * feature #20943 [DependencyInjection] Use current class as default class for factory declarations (ogizanagi) + * feature #21003 [Console][FrameworkBundle] Log console exceptions (jameshalsall, chalasr) + * feature #21313 [DI] Add Yaml syntax for short services definition (ogizanagi) + * feature #20694 [Cache] Implement PSR-16 SimpleCache v1.0 (nicolas-grekas) + * feature #21327 [DI] Factorize compiler passes around new AbstractRecursivePass (nicolas-grekas) + * feature #19086 [FrameworkBundle] add "mapping" configuration key at validation secti… (davewwww) + * feature #21350 [Yaml] Remove internal arguments from the api (GuilhemN) + * feature #21353 [ClassLoader] Deprecated the component (nicolas-grekas) + * feature #21334 [Workflow] Introduce concept of SupportStrategyInterface (andesk, lyrixx) + * feature #20390 [Ldap] added Ldap entry rename for ExtLdap adapter (fruitwasp) + * feature #21065 Added cache data collector and profiler page (Nyholm) + * feature #21306 [DependencyInjection] Always autowire the constructor (dunglas) + * feature #20493 [Debug] Trigger deprecation on `@final` annotation in DebugClassLoader - prepare making some classes final (GuilhemN) + * feature #21244 [DI] Remove synthetic services from methodMap + generated methods (nicolas-grekas) + * feature #21238 [VarDumper] Add search keyboard shortcuts (ogizanagi) + * feature #21290 [FrameworkBundle] Fix debug:container --show-arguments missing cases (chalasr) + * feature #21263 [DI] Mark generated containers as final (nicolas-grekas) + * feature #21253 [TwigBridge][Worklow] Added a new workflow_has_place function (Padam87, lyrixx) + * feature #21234 Add a new Dotenv component (fabpot) + * feature #20861 Add a --show-arguments flag to the debug:container command (Cydonia7) + * feature #21223 [DI] Deprecate case insentivity of service identifiers (nicolas-grekas) + * feature #20887 [Form] DateIntervalType: Allow to configure labels & enhance form theme (ogizanagi) + * feature #19443 [Console] Move AddConsoleCommandPass from FrameworkBundle to Console. (bcremer) + * feature #21231 [FrameworkBundle] allow to reference files directly from kernel.root_dir (fabpot) + * feature #20611 [DI] FileLoaders: Allow to explicit type to load (ogizanagi) + * feature #20689 [Config][FrameworkBundle] Allow to dump extension config reference sub-path (ogizanagi) + * feature #21188 [HttpFoundation] Add File\Stream for size-unknown BinaryFileResponse (nicolas-grekas) + * feature #21214 [DI] Allow ~ instead of {} for services in Yaml (wouterj) + * feature #20612 [Filesystem] Add appendToFile() (chalasr) + * feature #20612 [Filesystem] Add appendToFile() (chalasr) + * feature #21114 [Yaml] parse multi-line strings (xabbuh) + * feature #21196 [FrameworkBundle] changed some default configs from canBeEnabled to canBeDisabled (fabpot) + * feature #20937 [EventDispatcher] Deprecate ContainerAwareEventDispatcher (nicolas-grekas) + * feature #21190 [WebServerBundle] Decouple server commands from the container (chalasr) + * feature #21071 [DI] Add "inherit-tags" with configurable defaults + same for "public", "tags" & "autowire" (nicolas-grekas, ogizanagi) + * feature #21133 [DI] Optional class for named services (hason, nicolas-grekas) + * feature #20953 [DI][EventDispatcher] Add & wire closure-proxy argument type (nicolas-grekas) + * feature #20586 [Console] Ease writing to stderr using SymfonyStyle (chalasr) + * feature #20547 [FrameworkBundle] Allowed symlinks when searching for translation, searialization and validation files (tifabien) + * feature #20735 Deprecate ClassCollectionLoader and Kernel::loadClassCache (dbrumann) + * feature #21140 [PhpUnitBridge] deprecate the testLegacy test name prefix (xabbuh) + * feature #21109 [Profiler][VarDumper] Add a search feature to the HtmlDumper (ogizanagi) + * feature #21039 Web server bundle (fabpot) + * feature #20907 [DependencyInjection] Implement lazy collection type using generators (tgalopin, nicolas-grekas) + * feature #21075 [Console] Show hidden commands in json & xml descriptors (ogizanagi) + * feature #21129 [FrameworkBundle] Display original definition for aliases in debug:container (chalasr) + * feature #21108 [Cache] Add DSN, createClient & better error reporting to MemcachedAdapter (nicolas-grekas, robfrawley) + * feature #21147 [PhpUnitBridger] Bump simple-phpunit to PHPUnit 5.7 by default (nicolas-grekas) + * feature #21112 [PhpUnitBridge] run PHPUnit in the same process (xabbuh) + * feature #21106 [Validator] support DateTimeInterface instances for times (xabbuh) + * feature #20809 [FrameworkBundle] Display the controller class name in 'debug:router' (lyrixx) + * feature #21082 [Cache] TraceableAdapter (Nyholm) + * feature #20938 [DI] Prepare dropping "strict" handling in loaders (nicolas-grekas) + * feature #20971 [WebProfilerBundle] Split PHP version if needed (ro0NL) + * feature #20634 [DI] Deprecate dumping an uncompiled container (ro0NL) + * feature #20923 #20921 [Config] Provide shorthand methods for ArrayNodeDefinition::pr… (skafandri) + * feature #20569 [HttpFoundation] Create cookie from string + synchronize response cookies (ro0NL) + * feature #20618 [DI] Make ContainerBuilder::resolveEnvPlaceholders() able to inline the values of referenced env vars. (nicolas-grekas) + * feature #20962 Request exceptions (thewilkybarkid, fabpot) + * feature #20928 [FrameworkBundle] don't load translator services if not required (xabbuh) + * feature #20644 [HttpFoundation] Compute cookie max-age attribute (ro0NL) + * feature #20167 [DependencyInjection] Make method (setter) autowiring configurable (dunglas) + * feature #20663 [DependencyInjection] replace DefinitionDecorator by ChildDefinition (xabbuh) + * feature #20197 [WebProfilerBundle] Improve Ajax Profiling Performance (javascript) (patrick-mcdougle) + * feature #20487 [Console] Disallow inheritance from ProgressBar (a-ast) + * feature #20651 [DependencyInjection] Added Yaml syntax shortcut for name-only tags (wouterj) + * feature #20648 [DependencyInjection] Added a shortcut method for autowired definitions (wouterj) + * feature #20697 Updated the "PHP config" panel in the profiler (javiereguiluz) + * feature #20773 [FrameworkBundle] Added GlobalVariables::getToken() (HeahDude) + * feature #20866 [Console] Improve markdown format (ro0NL) + * feature #20867 [Console] Include application name/version in JSON descriptions (ro0NL) + * feature #20869 [Console] Improve UX on not found namespace/command (Seldaek) + * feature #20858 [Cache] Simple Memcached Adapter (robfrawley) + * feature #20881 [VarDumper] Add SymfonyCaster::castRequest() (nicolas-grekas) + * feature #20810 [FrameworkBundle] Allow clearing private cache pools in cache:pool:clear (chalasr) + * feature #20417 [SecurityBundle] Rename FirewallContext#getContext() (chalasr) + * feature #20801 [Security] deprecate the RoleInterface (xabbuh) + * feature #20260 [DependencyInjection] Support autowiring for EventDispatcher/EventDispatcherInterface (chalasr) + * feature #20777 [ClassLoader] Deprecate Apc/WinCache/Xcache class loaders (nicolas-grekas) + * feature #20524 [Serializer][XmlEncoder] Allow removing empty tags in generated XML (amoiraud) + * feature #19958 [Serializer] Throw exception when extra attributes are used during an object denor… (juliendidier) + * feature #20310 [Ldap] Allow search scoping (xunto) + * feature #18952 [Security] Add a JSON authentication listener (dunglas) + * feature #20161 add toolbar & profiler SVG style classes (havvg) + * feature #20467 [DomCrawler] Add support for formaction and formmethod attributes (stof) + * feature #20509 [Serializer] Allow to specify a single value in @Groups (dunglas) + * feature #20722 Updated the "Symfony Config" panel in the profiler (javiereguiluz) + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ae18925cb6e20..7902d9aff3a77 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,6 +5,7 @@ Symfony is an open source, community-driven project. If you'd like to contribute, please read the following documents: +* [Reviewing issues/pull requests][0] * [Reporting a Bug][1] * [Submitting a Patch][2] * [Symfony Core Team][3] @@ -14,6 +15,7 @@ If you'd like to contribute, please read the following documents: * [Coding Standards][7] * [Conventions][8] +[0]: https://symfony.com/doc/current/contributing/community/reviews.html [1]: https://symfony.com/doc/current/contributing/code/bugs.html [2]: https://symfony.com/doc/current/contributing/code/patches.html [3]: https://symfony.com/doc/current/contributing/code/core_team.html diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index c5fb26e0e91d8..a29c7ec45376b 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -15,29 +15,32 @@ Symfony is the result of the work of many people who made the code better - Johannes S (johannes) - Kris Wallsmith (kriswallsmith) - Jakub Zalas (jakubzalas) - - Ryan Weaver (weaverryan) - Kévin Dunglas (dunglas) + - Ryan Weaver (weaverryan) - Javier Eguiluz (javier.eguiluz) - Hugo Hamon (hhamon) - Abdellatif Ait boudad (aitboudad) - Pascal Borreli (pborreli) - Wouter De Jong (wouterj) - Romain Neutron (romain) + - Grégoire Pineau (lyrixx) - Joseph Bielawski (stloyd) - Karma Dordrak (drak) - Lukas Kahwe Smith (lsmith) - Martin Hasoň (hason) + - Robin Chalas (chalas_r) + - Maxime Steinhausser (ogizanagi) - Jeremy Mikola (jmikola) - Jean-François Simon (jfsimon) - - Grégoire Pineau (lyrixx) - Benjamin Eberlei (beberlei) - Igor Wiedler (igorw) - Eriksen Costa (eriksencosta) - Jules Pietri (heah) - Sarah Khalil (saro0h) - - Maxime Steinhausser (ogizanagi) - Jonathan Wage (jwage) + - Guilhem Niot (energetick) - Diego Saint Esteben (dosten) + - Roland Franssen (ro0) - Alexandre Salomé (alexandresalome) - William Durand (couac) - ornicar @@ -45,79 +48,80 @@ Symfony is the result of the work of many people who made the code better - stealth35 ‏ (stealth35) - Alexander Mols (asm89) - Bulat Shakirzyanov (avalanche123) - - Robin Chalas (chalas_r) + - Peter Rehm (rpet) - Saša Stamenković (umpirsky) - Henrik Bjørnskov (henrikbjorn) - Miha Vrhovnik + - Roland Franssen (ro0) - Diego Saint Esteben (dii3g0) - - Ener-Getick (energetick) - Konstantin Kudryashov (everzet) - Bilal Amarni (bamarni) - Florin Patan (florinpatan) - - Peter Rehm (rpet) - - Iltar van der Berg (kjarli) + - Matthias Pigulla (mpdude) - Kevin Bond (kbond) - Andrej Hudec (pulzarraider) - Gábor Egyed (1ed) - Michel Weimerskirch (mweimerskirch) - Eric Clemmons (ericclemmons) - Charles Sarrazin (csarrazi) - - Matthias Pigulla (mpdude) + - Pierre du Plessis (pierredup) - Christian Raue - Arnout Boks (aboks) - Deni - Henrik Westphal (snc) - Dariusz Górecki (canni) + - Titouan Galopin (tgalopin) - Douglas Greenshields (shieldo) - Konstantin Myakshin (koc) - Lee McDermott - Brandon Turner - Luis Cordova (cordoval) + - Jáchym Toušek (enumag) - Graham Campbell (graham) - - Titouan Galopin (tgalopin) - Daniel Holmes (dholmes) - - Pierre du Plessis (pierredup) + - Toni Uebernickel (havvg) - Bart van den Burg (burgov) - Jordan Alliot (jalliot) + - Jérémy DERUSSÉ (jderusse) - John Wards (johnwards) - - Toni Uebernickel (havvg) - Fran Moreno (franmomu) - Antoine Hérault (herzult) - Paráda József (paradajozsef) - - Roland Franssen (ro0) - Dariusz Ruminski - - Jáchym Toušek (enumag) - Arnaud Le Blanc (arnaud-lb) - Jérôme Tamarelle (gromnan) + - Maxime STEINHAUSSER - Michal Piotrowski (eventhorizon) - Tim Nagel (merk) + - Issei Murasawa (issei_m) - Brice BERNARD (brikou) - Alexander M. Turek (derrabus) + - Baptiste Clavié (talus) - marc.weistroff - - Issei Murasawa (issei_m) - lenar - Włodzimierz Gajda (gajdaw) - - Baptiste Clavié (talus) + - Vladimir Reznichenko (kalessil) - Alexander Schwenn (xelaris) - Florian Voutzinos (florianv) - Colin Frei - Adrien Brault (adrienbrault) - Joshua Thijssen - Peter Kokot (maastermedia) + - David Buchmann (dbu) - excelwebzone - Jacob Dreesen (jdreesen) - - Jérémy DERUSSÉ (jderusse) - - Vladimir Reznichenko (kalessil) + - Tobias Nyholm (tobias) - Tomáš Votruba (tomas_votruba) - Fabien Pennequin (fabienpennequin) - Gordon Franke (gimler) - Eric GELOEN (gelo) - - David Buchmann (dbu) + - Daniel Wehner (dawehner) - Tugdual Saunier (tucksaun) - Théo FIDRY (theofidry) - Robert Schönthal (digitalkaoz) - Florian Lonqueu-Brochard (florianlb) - Stefano Sala (stefano.sala) + - Yonel Ceruto González (yonelceruto) - Evgeniy (ewgraf) - Juti Noppornpitak (shiroyuki) - Tigran Azatyan (tigranazatyan) @@ -132,7 +136,7 @@ Symfony is the result of the work of many people who made the code better - Rafael Dohms (rdohms) - Arnaud Kleinpeter (nanocom) - jwdeitch - - Tobias Nyholm (tobias) + - Mikael Pajunen - Joel Wurtz (brouznouf) - Philipp Wahala (hifi) - Vyacheslav Pavlov @@ -142,11 +146,10 @@ Symfony is the result of the work of many people who made the code better - Vincent AUBERT (vincent) - Rouven Weßling (realityking) - Teoh Han Hui (teohhanhui) - - Mikael Pajunen - Clemens Tolboom - Helmer Aaviksoo + - Grégoire Paris (greg0ire) - Hiromi Hishida (77web) - - Yonel Ceruto González (yonelceruto) - Richard van Laak (rvanlaak) - Matthieu Ouellette-Vachon (maoueh) - Michał Pipa (michal.pipa) @@ -154,23 +157,26 @@ Symfony is the result of the work of many people who made the code better - Jonathan Ingram (jonathaningram) - Artur Kotyrba - jeremyFreeAgent (Jérémy Romey) (jeremyfreeagent) + - James Halsall (jaitsu) - Warnar Boekkooi (boekkooi) - Dmitrii Chekaliuk (lazyhammer) - Clément JOBEILI (dator) - - Daniel Wehner + - Dawid Nowak - Possum - Dorian Villet (gnutix) - Richard Miller (mr_r_miller) - Mario A. Alvarez Garcia (nomack84) - Dennis Benkert (denderello) - Benjamin Dulau (dbenjamin) + - James Halsall (jaitsu) - Mathieu Lemoine (lemoinem) + - Chris Wilkinson (thewilkybarkid) - Andreas Hucks (meandmymonkey) - Noel Guilbert (noel) - Lars Strojny (lstrojny) - - Maxime STEINHAUSSER - Stepan Anchugov (kix) - bronze1man + - Daniel Espendiller - sun (sun) - Larry Garfield (crell) - Martin Schuhfuß (usefulthink) @@ -179,6 +185,7 @@ Symfony is the result of the work of many people who made the code better - fivestar - Dominique Bongiraud - Jeremy Livingston (jeremylivingston) + - Michael Lee (zerustech) - Matthieu Auger (matthieuauger) - Leszek Prabucki (l3l0) - François Zaninotto (fzaninotto) @@ -187,14 +194,17 @@ Symfony is the result of the work of many people who made the code better - John Kary (johnkary) - Justin Hileman (bobthecow) - Blanchon Vincent (blanchonvincent) + - Christian Schmidt - Michele Orselli (orso) - Tom Van Looy (tvlooy) - Sven Paulus (subsven) - Rui Marinho (ruimarinho) - - Daniel Espendiller - - Dawid Nowak + - SpacePossum - Eugene Wissner - Julien Brochet (mewt) + - Julien Falque (julienfalque) + - Tristan Darricau (nicofuma) + - Grégoire Paris (greg0ire) - Sergey Linnik (linniksa) - Michaël Perrin (michael.perrin) - Marcel Beerta (mazen) @@ -202,9 +212,9 @@ Symfony is the result of the work of many people who made the code better - Jannik Zschiesche (apfelbox) - Marco Pivetta (ocramius) - julien pauli (jpauli) - - Michael Lee (zerustech) - Lorenz Schori - Sébastien Lavoie (lavoiesl) + - David Maicher (dmaicher) - Francois Zaninotto - Alexander Kotynia (olden) - Daniel Tschinder @@ -213,29 +223,27 @@ Symfony is the result of the work of many people who made the code better - Manuel Reinhard (sprain) - Danny Berger (dpb587) - Jérôme Vasseur + - Adam Prager (padam87) - Roman Marintšenko (inori) - - Christian Schmidt - Xavier Montaña Carreras (xmontana) - - Chris Wilkinson (thewilkybarkid) - Mickaël Andrieu (mickaelandrieu) - Xavier Perez - Arjen Brouwer (arjenjb) - Katsuhiro OGAWA - - James Halsall (jaitsu) + - Patrick McDougle (patrick-mcdougle) - Alif Rachmawadi - Kristen Gilden (kgilden) - Pierre-Yves LEBECQ (pylebecq) - Alex Pott - Jakub Kucharovic (jkucharovic) + - Uwe Jäger (uwej711) - Eugene Leonovich (rybakit) - Filippo Tessarotto - - Tristan Darricau (nicofuma) - Joseph Rouff (rouffj) - Félix Labrecque (woodspire) - GordonsLondon - Jan Sorgalla (jsor) - Ray - - Grégoire Paris (greg0ire) - Leo Feyer - Chekote - Thomas Adam @@ -243,6 +251,7 @@ Symfony is the result of the work of many people who made the code better - Jhonny Lidfors (jhonne) - Diego Agulló (aeoris) - jdhoek + - Pavel Batanov (scaytrase) - Nikita Konstantinov - Wodor Wodorski - Thomas Lallement (raziel057) @@ -251,7 +260,6 @@ Symfony is the result of the work of many people who made the code better - Beau Simensen (simensen) - Michael Hirschler (mvhirsch) - Robert Kiss (kepten) - - Ruben Gonzalez (rubenrua) - Roumen Damianoff (roumen) - Antonio J. García Lagar (ajgarlag) - Kim Hemsø Rasmussen (kimhemsoe) @@ -259,13 +267,12 @@ Symfony is the result of the work of many people who made the code better - Peter Kruithof (pkruithof) - Michael Holm (hollo) - Marc Weistroff (futurecat) - - SpacePossum + - Christian Schmidt - Hidde Wieringa (hiddewie) - Chris Smith (cs278) - Florian Klein (docteurklein) - Oleg Voronkovich - Manuel Kiessling (manuelkiessling) - - Daniel Wehner - Atsuhiro KUBO (iteman) - Andrew Moore (finewolf) - Bertrand Zuchuat (garfield-fr) @@ -275,8 +282,9 @@ Symfony is the result of the work of many people who made the code better - Andrey Esaulov (andremaha) - Grégoire Passault (gregwar) - Ismael Ambrosi (iambrosi) - - Uwe Jäger (uwej711) + - Baptiste Lafontaine - Aurelijus Valeiša (aurelijus) + - Victor Bocharsky (bocharsky_bw) - Jan Decavele (jandc) - Gustavo Piltcher - Stepan Tanasiychuk (stfalcon) @@ -304,6 +312,7 @@ Symfony is the result of the work of many people who made the code better - Magnus Nordlander (magnusnordlander) - alquerci - Francesco Levorato + - Rob Frawley 2nd (robfrawley) - Vitaliy Zakharov (zakharovvi) - Tobias Sjösten (tobiassjosten) - Gyula Sallai (salla) @@ -323,6 +332,7 @@ Symfony is the result of the work of many people who made the code better - Clément Gautier (clementgautier) - Eduardo Gulias (egulias) - giulio de donato (liuggio) + - ShinDarth - Stéphane PY (steph_py) - Philipp Kräutli (pkraeutli) - Kirill chEbba Chebunin (chebba) @@ -331,14 +341,18 @@ Symfony is the result of the work of many people who made the code better - Loïc Chardonnet (gnusat) - Marek Kalnik (marekkalnik) - Vyacheslav Salakhutdinov (megazoll) + - Jerzy Zawadzki (jzawadzki) - Hassan Amouhzi + - gadelat (gadelat) - Tamas Szijarto - Pavel Volokitin (pvolok) + - François Pluchino (francoispluchino) + - Nicolas Dewez (nicolas_dewez) - Endre Fejes - Tobias Naumann (tna) - Daniel Beyer - Shein Alexey - - Baptiste Lafontaine + - Romain Gautier (mykiwi) - Joe Lencioni - Daniel Tschinder - Kai @@ -351,7 +365,6 @@ Symfony is the result of the work of many people who made the code better - Xavier HAUSHERR - Albert Jessurum (ajessu) - Laszlo Korte - - Pavel Batanov (scaytrase) - Miha Vrhovnik - Alessandro Desantis - hubert lecorche (hlecorche) @@ -378,20 +391,23 @@ Symfony is the result of the work of many people who made the code better - Mihai Stancu - Olivier Dolbeau (odolbeau) - Jan Rosier (rosier) + - Thomas Royer (cydonia7) + - Josip Kruslin - vagrant - EdgarPE - Florian Pfitzer (marmelatze) - Asier Illarramendi (doup) + - Andreas Braun - Chris Sedlmayr (catchamonkey) - Seb Koelen + - Dany Maillard (maidmaid) - Christoph Mewes (xrstf) - Vitaliy Tverdokhlib (vitaliytv) - Ariel Ferrandini (aferrandini) - Dirk Pahl (dirkaholic) - cedric lombardot (cedriclombardot) - - David Maicher (dmaicher) - Jonas Flodén (flojon) - - Christian Schmidt + - Amrouche Hamza - Marcin Sikoń (marphi) - Dominik Zogg (dominik.zogg) - Marek Pietrzak @@ -401,6 +417,7 @@ Symfony is the result of the work of many people who made the code better - Gintautas Miselis - Rob Bast - David Badura (davidbadura) + - Jordan Samouh (jordansamouh) - Zander Baldwin - Adam Harvey - Alex Bakhturin @@ -408,6 +425,8 @@ Symfony is the result of the work of many people who made the code better - boombatower - Fabrice Bernhard (fabriceb) - Jérôme Macias (jeromemacias) + - Andrey Astakhov (aast) + - Thomas Calvet - Fabian Lange (codingfabian) - Frank Neff (fneff) - Roman Lapin (memphys) @@ -418,6 +437,7 @@ Symfony is the result of the work of many people who made the code better - Pablo Díez (pablodip) - Kevin McBride - Sergio Santoro + - Robin van der Vleuten (robinvdvleuten) - Philipp Rieber (bicpi) - Manuel de Ruiter (manuel) - Eduardo Oliveira (entering) @@ -427,7 +447,7 @@ Symfony is the result of the work of many people who made the code better - Roy Van Ginneken (rvanginneken) - ondrowan - Barry vd. Heuvel (barryvdh) - - Jerzy Zawadzki (jzawadzki) + - Wouter J - Evan S Kaufman (evanskaufman) - mcben - Jérôme Vieilledent (lolautruche) @@ -437,16 +457,18 @@ Symfony is the result of the work of many people who made the code better - Markus Lanthaler (lanthaler) - Remi Collet - Vicent Soria Durá (vicentgodella) - - Nicolas Dewez (nicolas_dewez) - Anthony Ferrara - - Victor Bocharsky (bocharsky_bw) - Ioan Negulescu - Jakub Škvára (jskvara) - Andrew Udvare (audvare) - alexpods + - Nikolay Labinskiy (e-moe) + - Arjen van der Meijden - Michele Locati + - Dariusz Ruminski - Erik Trapman (eriktrapman) - De Cock Xavier (xdecock) + - Arthur de Moulins (4rthem) - Almog Baku (almogbaku) - Scott Arciszewski - Norbert Orzechowicz (norzechowicz) @@ -477,23 +499,26 @@ Symfony is the result of the work of many people who made the code better - Alexander Deruwe (aderuwe) - Alain Hippolyte (aloneh) - Dave Hulbert (dave1010) - - François Pluchino (francoispluchino) - Ivan Rey (ivanrey) - Marcin Chyłek (songoq) - Ned Schwartz - Ziumin - Jeremy Benoist - Lenar Lõhmus + - Sander Toonen (xatoo) - Benjamin Laugueux (yzalis) - Zach Badgett (zachbadgett) - Aurélien Fredouelle + - Jérôme Parmentier (lctrs) - Pavel Campr (pcampr) - Johnny Robeson (johnny) - Disquedur - Michiel Boeckaert (milio) - Geoffrey Tran (geoff) + - Romain Pierre (romain-pierre) - Jan Behrens - Mantas Var (mvar) + - Frank de Jonge (frenkynet) - Sebastian Krebs - Jean-Christophe Cuvelier [Artack] - Christopher Davis (chrisguitarguy) @@ -510,16 +535,18 @@ Symfony is the result of the work of many people who made the code better - Konstantin S. M. Möllers (ksmmoellers) - Sinan Eldem - Alexandre Dupuy (satchette) + - Andre Rømcke (andrerom) - Nahuel Cuesta (ncuesta) - Chris Boden (cboden) - Asmir Mustafic (goetas) - Stefan Gehrig (sgehrig) - - Josip Kruslin - Hany el-Kerdany - Wang Jingyu - Åsmund Garfors - Maxime Douailin - Jean Pasdeloup (pasdeloup) + - Benjamin Cremer (bcremer) + - Thierry Thuon (lepiaf) - Javier López (loalf) - Reinier Kip - Geoffrey Brier (geoffrey-brier) @@ -533,6 +560,7 @@ Symfony is the result of the work of many people who made the code better - Kamil Kokot (pamil) - Aurimas Niekis (gcds) - Max Grigorian (maxakawizard) + - mcfedr (mcfedr) - Rostyslav Kinash - Maciej Malarz (malarzm) - Daisuke Ohata @@ -541,15 +569,20 @@ Symfony is the result of the work of many people who made the code better - maxime.steinhausser - Stefan Warman - Tristan Maindron (tmaindron) + - Wesley Lancel - Ke WANG (yktd26) - Strate - Miquel Rodríguez Telep (mrtorrent) - Sergey Kolodyazhnyy (skolodyazhnyy) - umpirski + - Denis Brumann (dbrumann) + - Quentin de Longraye (quentinus95) - Chris Heng (gigablah) + - Richard Bradley - Ulumuddin Yunus (joenoez) - Luc Vieillescazes (iamluc) - Johann Saunier (prophet777) + - Samuel ROZE (sroze) - Michael Devery (mickadoo) - Antoine Corcy - Artur Eshenbrener @@ -569,6 +602,7 @@ Symfony is the result of the work of many people who made the code better - Disparity - origaminal - Matteo Beccati (matteobeccati) + - Kevin (oxfouzer) - Paweł Wacławczyk (pwc) - Oleg Zinchenko (cystbear) - Johannes Klauss (cloppy) @@ -588,7 +622,9 @@ Symfony is the result of the work of many people who made the code better - Michael Piecko - yclian - twifty + - Indra Gunawan (guind) - Peter Ward + - Julien DIDIER (juliendidier) - Dominik Ritter (dritter) - Sebastian Grodzicki (sgrodzicki) - Martin Hujer (martinhujer) @@ -597,12 +633,12 @@ Symfony is the result of the work of many people who made the code better - Vladyslav Petrovych - Alex Xandra Albert Sim - Carson Full - - Andrey Astakhov (aast) - Trent Steel (trsteel88) - Yuen-Chi Lian - Besnik Br - Dariusz Ruminski - Joshua Nye + - Claudio Zizza - Dave Marshall (davedevelopment) - avorobiev - Venu @@ -613,6 +649,7 @@ Symfony is the result of the work of many people who made the code better - michaelwilliams - 1emming - Leevi Graham (leevigraham) + - Nykopol (nykopol) - Jordan Deitch - Casper Valdemar Poulsen - Josiah (josiah) @@ -620,8 +657,8 @@ Symfony is the result of the work of many people who made the code better - John Bohn (jbohn) - Marc Morera (mmoreram) - Andrew Hilobok (hilobok) + - Noah Heck (myesain) - Christian Soronellas (theunic) - - Romain Gautier (mykiwi) - Yosmany Garcia (yosmanyga) - Wouter de Wild - Miroslav Sustek @@ -632,11 +669,14 @@ Symfony is the result of the work of many people who made the code better - Xavier Lacot (xavier) - possum - Denis Zunke (donalberto) + - Ahmed TAILOULOUTE (ahmedtai) - Olivier Maisonneuve (olineuve) - Masterklavi - Francis Turmel (fturmel) + - Nikita Nefedov (nikita2206) - cgonzalez - Ben + - Vincent Composieux (eko) - Jayson Xu (superjavason) - Jaik Dean (jaikdean) - fago @@ -647,6 +687,7 @@ Symfony is the result of the work of many people who made the code better - James Michael DuPont - Tom Klingenberg - Christopher Hall (mythmakr) + - Patrick Dawkins (pjcdawkins) - Paul Kamer (pkamer) - Rafał Wrzeszcz (rafalwrzeszcz) - Reen Lokum @@ -655,10 +696,10 @@ Symfony is the result of the work of many people who made the code better - Pierre Vanliefland (pvanliefland) - Sofiane HADDAG (sofhad) - frost-nzcr4 - - Arjen van der Meijden - Abhoryo - Fabian Vogler (fabian) - Korvin Szanto + - Arjan Keeman - Alaattin Kahramanlar (alaattin) - Sergey Zolotov (enleur) - Maksim Kotlyar (makasim) @@ -676,8 +717,10 @@ Symfony is the result of the work of many people who made the code better - Ivan Menshykov - David Romaní - Patrick Allaert + - Fabien Bourigault (fbourigault) - Gustavo Falco (gfalco) - Matt Robinson (inanimatt) + - Ruud Kamphuis (ruudk) - Aleksey Podskrebyshev - Calin Mihai Pristavu - David Marín Carreño (davefx) @@ -690,13 +733,13 @@ Symfony is the result of the work of many people who made the code better - corphi - grizlik - Derek ROTH + - Dmytro Boiko (eagle) - Shin Ohno (ganchiku) - Geert De Deckere (geertdd) - Jan Kramer (jankramer) - abdul malik ikhsan (samsonasik) - Henry Snoek (snoek09) - Simone Di Maulo (toretto460) - - Sander Toonen (xatoo) - Christian Morgan - Alexander Miehe (engerim) - Morgan Auchede (mauchede) @@ -727,7 +770,6 @@ Symfony is the result of the work of many people who made the code better - Mikhail Yurasov (mym) - LOUARDI Abdeltif (ouardisoft) - Robert Gruendler (pulse00) - - Robin van der Vleuten (robinvdvleuten) - Simon Terrien (sterrien) - Benoît Merlet (trompette) - Koen Kuipers @@ -756,11 +798,12 @@ Symfony is the result of the work of many people who made the code better - Colin O'Dell (colinodell) - xaav - Mahmoud Mostafa (mahmoud) + - Alessandro Lai - Pieter - Michael Tibben - Sander Marechal - - Andre Rømcke (andrerom) - Radosław Benkel + - jean pasqualini (darkilliant) - ttomor - Mei Gwilym (meigwilym) - Michael H. Arieli (excelwebzone) @@ -769,8 +812,8 @@ Symfony is the result of the work of many people who made the code better - fabios - Sander Coolen (scoolen) - Nicolas Le Goff (nlegoff) - - Andreas Braun - Ben Oman + - Andreas Kleemann - Manuele Menozzi - Anton Babenko (antonbabenko) - Irmantas Šiupšinskas (irmantas) @@ -778,9 +821,9 @@ Symfony is the result of the work of many people who made the code better - Zachary Tong (polyfractal) - Hryhorii Hrebiniuk - Dennis Fridrich (dfridrich) - - mcfedr (mcfedr) - hamza - dantleech + - Bastien DURAND (deamon) - Xavier Leune - Tero Alén (tero) - DerManoMann @@ -789,6 +832,7 @@ Symfony is the result of the work of many people who made the code better - boite - MGDSoft - Vadim Tyukov (vatson) + - David Wolter (davewww) - Sortex - chispita - Wojciech Sznapka @@ -797,6 +841,7 @@ Symfony is the result of the work of many people who made the code better - Máximo Cuadros (mcuadros) - tamirvs - julien.galenski + - Bob van de Vijver - Christian Neff - Per Sandström (per) - Goran Juric @@ -804,17 +849,21 @@ Symfony is the result of the work of many people who made the code better - Nicolas Macherey - Lin Clark - Jeremy David (jeremy.david) + - Robin Lehrmann (robinlehrmann) - Troy McCabe - Ville Mattila + - ilyes kooli + - gr1ev0us - Boris Vujicic (boris.vujicic) - Max Beutel + - Antanas Arvasevicius - nacho - Piotr Antosik (antek88) - Artem Lopata - - Samuel ROZE (sroze) - Sergey Novikov (s12v) - Marcos Quesada (marcos_quesada) - Matthew Vickery (mattvick) + - Angel Koilov (po_taka) - Dan Finnie - Ken Marfilla (marfillaster) - benatespina (benatespina) @@ -825,12 +874,15 @@ Symfony is the result of the work of many people who made the code better - Christian - Sergii Smertin (nfx) - hugofonseca (fonsecas72) + - Martynas Narbutas - Bailey Parker - Eddie Jaoude + - Antanas Arvasevicius - Haritz Iturbe (hizai) - Nerijus Arlauskas (nercury) - SPolischook - Diego Sapriza + - Anton A. Sumin - Joan Cruz - inspiran - Cristobal Dabed @@ -839,9 +891,9 @@ Symfony is the result of the work of many people who made the code better - Alex Demchenko (pilot) - Tadas Gliaubicas (tadcka) - Benoit Garret - - Thomas Royer (cydonia7) - DerManoMann - Olaf Klischat + - orlovv - Jhonny Lidfors (jhonny) - Julien Bianchi (jubianchi) - Robert Meijers @@ -859,7 +911,6 @@ Symfony is the result of the work of many people who made the code better - rpg600 - Péter Buri (burci) - Davide Borsatto (davide.borsatto) - - Indra Gunawan (guind) - kaiwa - Charles Sanquer (csanquer) - Albert Ganiev (helios-ag) @@ -877,6 +928,7 @@ Symfony is the result of the work of many people who made the code better - Taras Girnyk - Eduardo García Sanz (coma) - James Gilliland + - fduch (fduch) - Rhodri Pugh (rodnaph) - David de Boer (ddeboer) - Klaus Purer @@ -900,16 +952,19 @@ Symfony is the result of the work of many people who made the code better - Krzysztof Przybyszewski - Paul Matthews - Juan Traverso + - Tarjei Huse (tarjei) - Philipp Strube - Christian Sciberras - Clement Herreman (clemherreman) - Dan Ionut Dumitriu (danionut90) + - Vladislav Rastrusny (fractalizer) - Nyro (nyro) - Marco - Marc Torres - Alberto Aldegheri - heccjj - Alexandre Melard + - Jay Klehr - Sergey Yuferev - Tobias Stöckler - Mario Young @@ -922,7 +977,9 @@ Symfony is the result of the work of many people who made the code better - Xavier Coureau - ConneXNL - Aharon Perkel + - matze - Abdul.Mohsen B. A. A + - Martin Auswöger - Benoît Burnichon - pthompson - Malaney J. Hill @@ -950,6 +1007,7 @@ Symfony is the result of the work of many people who made the code better - Sebastian Utz - Adrien Gallou (agallou) - Karol Sójko (karolsojko) + - Grzegorz Zdanowski (kiler129) - sl_toto (sl_toto) - Walter Dal Mut (wdalmut) - Albin Kerouaton @@ -962,10 +1020,14 @@ Symfony is the result of the work of many people who made the code better - Samuel Vogel (samuelvogel) - Berat Doğan - Juanmi Rodriguez Cerón + - Andy Raines - Anthony Ferrara - Klaas Cuvelier (kcuvelier) + - markusu49 - Steve Frécinaux + - Jules Lamur - ShiraNai7 + - Markus Fasselt (digilist) - Vašek Purchart (vasek-purchart) - Janusz Jabłoński (yanoosh) - Sandro Hopf @@ -987,21 +1049,22 @@ Symfony is the result of the work of many people who made the code better - Alberto Pirovano (geezmo) - Pete Mitchell (peterjmit) - Tom Corrigan (tomcorrigan) + - Luis Galeas - Martin Pärtel - - Noah Heck (myesain) + - George Mponos (gmponos) - Patrick Daley (padrig) - Xavier Briand (xavierbriand) - Max Summe - WedgeSama - Felds Liscia - - Ahmed TAILOULOUTE (ahmedtai) - Maxime Veber (nek-) - Sullivan SENECHAL - Tadcka - Beth Binkovitz + - Gonzalo Míguez - Romain Geissler + - Adrien Moiruad - Tomaz Ahlin - - Benjamin Cremer (bcremer) - Marcus Stöhr (dafish) - Emmanuel Vella (emmanuel.vella) - Carsten Nielsen (phreaknerd) @@ -1015,15 +1078,16 @@ Symfony is the result of the work of many people who made the code better - Max Romanovsky (maxromanovsky) - Mathieu Morlon - Daniel Tschinder - - Nykopol (nykopol) - Rafał Muszyński (rafmus90) - Timothy Anido (xanido) - Rick Prent + - skalpa - Martin Eckhardt - Pieter Jordaan - Damien Tournoud - Jon Gotlin (jongotlin) - Michael Dowling (mtdowling) + - Karlos Presumido (oneko) - BilgeXA - r1pp3rj4ck - Robert Queck @@ -1057,7 +1121,6 @@ Symfony is the result of the work of many people who made the code better - Konrad Mohrfeldt - Lance Chen - Andrew (drew) - - Nikolay Labinskiy (e-moe) - kor3k kor3k (kor3k) - Stelian Mocanita (stelian) - Flavian (2much) @@ -1066,7 +1129,6 @@ Symfony is the result of the work of many people who made the code better - Mephistofeles - Hoffmann András - Olivier - - Wesley Lancel - pscheit - Zdeněk Drahoš - Dan Harper @@ -1093,12 +1155,10 @@ Symfony is the result of the work of many people who made the code better - Adrian Olek (adrianolek) - Przemysław Piechota (kibao) - Leonid Terentyev (li0n) - - Adam Prager (padam87) - ryunosuke - victoria - Francisco Facioni (fran6co) - Iwan van Staveren (istaveren) - - Dany Maillard (maidmaid) - Povilas S. (povilas) - pborreli - Eric Caron @@ -1125,6 +1185,7 @@ Symfony is the result of the work of many people who made the code better - Michal Gebauer - Gleb Sidora - David Stone + - Niels Keurentjes (curry684) - Jovan Perovic (jperovic) - Pablo Maria Martelletti (pmartelletti) - Yassine Guedidi (yguedidi) @@ -1140,6 +1201,7 @@ Symfony is the result of the work of many people who made the code better - Dennis Væversted - nuncanada - flack + - izzyp - František Bereň - Christoph Nissle (derstoffel) - Ionel Scutelnicu (ionelscutelnicu) @@ -1152,6 +1214,7 @@ Symfony is the result of the work of many people who made the code better - Julius Beckmann - Romain Dorgueil - Grayson Koonce (breerly) + - Fabien LUCAS (flucas2) - Karim Cassam Chenaï (ka) - Nicolas Bastien (nicolas_bastien) - Denis (yethee) @@ -1159,6 +1222,7 @@ Symfony is the result of the work of many people who made the code better - Andy Stanberry - Luiz “Felds” Liscia - Thomas Rothe + - nietonfir - alefranz - avi123 - alsar @@ -1179,6 +1243,7 @@ Symfony is the result of the work of many people who made the code better - Brian Graham (incognito) - Kevin Vergauwen (innocenzo) - Alessio Baglio (ioalessio) + - Johannes Müller (johmue) - Jordi Llonch (jordillonch) - Cédric Dugat (ph3nol) - Philip Dahlstrøm (phidah) @@ -1200,11 +1265,11 @@ Symfony is the result of the work of many people who made the code better - Bram Van der Sype (brammm) - Guile (guile) - Julien Moulin (lizjulien) - - Nikita Nefedov (nikita2206) - Mauro Foti (skler) - Yannick Warnier (ywarnier) - Kevin Decherf - Jason Woods + - klemens - dened - Dmitry Korotovsky - Michael van Tricht @@ -1220,10 +1285,13 @@ Symfony is the result of the work of many people who made the code better - Andrey Chernykh - Edvinas Klovas - Drew Butler + - Peter Breuls - Tischoi - J Bruni - Alexey Prilipko + - vlakoff - bertillon + - Bertalan Attila - Yannick Bensacq (cibou) - Luca Genuzio (genuzio) - Hans Nilsson (hansnilsson) @@ -1237,12 +1305,12 @@ Symfony is the result of the work of many people who made the code better - Jérémy M (th3mouk) - Vincent LEFORT (vlefort) - Sadicov Vladimir (xtech) + - Kevin EMO (zarcox) - Alexander Zogheb - Rémi Blaise - Joel Marcey - David Christmann - root - - Wouter J - James Hudson - Tom Maguire - David Zuelke @@ -1250,15 +1318,15 @@ Symfony is the result of the work of many people who made the code better - adenkejawen - Ari Pringle (apringle) - Dan Ordille (dordille) - - Dmytro Boiko (eagle) - Jan Eichhorn (exeu) - Grégory Pelletier (ip512) - John Nickell (jrnickell) - - Julien DIDIER (juliendidier) - Martin Mayer (martin) - Grzegorz Łukaszewicz (newicz) + - Jonny Schmid (schmidjon) - Götz Gottwald - Veres Lajos + - Michael Babker - grifx - Robert Campbell - Matt Lehner @@ -1284,6 +1352,7 @@ Symfony is the result of the work of many people who made the code better - Alan Chen - Maerlyn - Even André Fiskvik + - Arjan Keeman - Erik van Wingerden - Dane Powell - Gerrit Drost @@ -1309,7 +1378,6 @@ Symfony is the result of the work of many people who made the code better - David Joos (djoos) - Denis Klementjev (dklementjev) - Tomáš Polívka (draczris) - - Vincent Composieux (eko) - Franz Liedke (franzliedke) - Christophe BECKER (goabonga) - gondo (gondo) @@ -1319,18 +1387,20 @@ Symfony is the result of the work of many people who made the code better - Jelle Bekker (jbekker) - Ian Jenkins (jenkoian) - Jorge Martin (jorgemartind) - - Julien Falque (julienfalque) + - Joeri Verdeyen (jverdeyen) - Kevin Herrera (kherge) - Luis Ramón López López (lrlopez) - Muriel (metalmumu) - Michael Pohlers (mick_the_big) + - mlpo (mlpo) - Cayetano Soriano Gallego (neoshadybeat) - Ondrej Machulda (ondram) - - Patrick McDougle (patrick-mcdougle) - Pablo Monterde Perez (plebs) - Jimmy Leger (redpanda) + - Marcin Szepczynski (szepczynski) - Cyrille Jouineau (tuxosaurus) - Yorkie Chadwick (yorkie76) + - GuillaumeVerdon - Yanick Witschi - Ondrej Mirtes - akimsko @@ -1341,7 +1411,9 @@ Symfony is the result of the work of many people who made the code better - Saem Ghani - Stefan Oderbolz - Curtis + - Gabriel Moreira - Alexey Popkov + - ChS - Joseph Deray - Damian Sromek - Ben @@ -1350,6 +1422,7 @@ Symfony is the result of the work of many people who made the code better - Arnaud Buathier (arnapou) - chesteroni (chesteroni) - Mauricio Lopez (diaspar) + - HADJEDJ Vincent (hadjedjvincent) - Daniele Cesarini (ijanki) - Ismail Asci (ismailasci) - Simon CONSTANS (kosssi) @@ -1362,6 +1435,7 @@ Symfony is the result of the work of many people who made the code better - Wotre - goohib - Xavier HAUSHERR + - Edwin Hageman - Mantas Urnieža - Cas - Dusan Kasan @@ -1378,7 +1452,9 @@ Symfony is the result of the work of many people who made the code better - znerol - Christian Eikermann - Antonio Angelino + - Matt Fields - Shawn Iwinski + - Niklas Keller - Vladimir Sazhin - lol768 - jamogon @@ -1393,6 +1469,7 @@ Symfony is the result of the work of many people who made the code better - Dariusz Czech - Anonymous User - Eric J. Duran + - Alexandru Bucur - cmfcmf - Drew Butler - Steve Müller @@ -1466,6 +1543,7 @@ Symfony is the result of the work of many people who made the code better - Lin Lu - arduanov - sualko + - Bilge - Nicolas Roudaire - Alfonso (afgar) - Andreas Forsblom (aforsblo) @@ -1477,18 +1555,20 @@ Symfony is the result of the work of many people who made the code better - Daniel Basten (axhm3a) - Bill Hance (billhance) - Bernd Matzner (bmatzner) + - Bram Tweedegolf (bram_tweedegolf) - Choong Wei Tjeng (choonge) - Kousuke Ebihara (co3k) - Loïc Vernet (coil) + - Christian Gripp (core23) - Christoph Schaefer (cvschaefer) - Damon Jones (damon__jones) + - Łukasz Giza (destroyer) - Daniel Londero (dlondero) - Sebastian Landwehr (dword123) - Adel ELHAIBA (eadel) - Damián Nohales (eagleoneraptor) - Elliot Anderson (elliot) - Fabien D. (fabd) - - Fabien Bourigault (fbourigault) - Carsten Eilers (fnc) - Sorin Gitlan (forapathy) - Yohan Giarelli (frequence-web) @@ -1497,6 +1577,8 @@ Symfony is the result of the work of many people who made the code better - Arash Tabriziyan (ghost098) - ibasaw (ibasaw) - Vladislav Krupenkin (ideea) + - Imangazaliev Muhammad (imangazaliev) + - j0k (j0k) - joris de wit (jdewit) - Jérémy CROMBEZ (jeremy) - Jose Manuel Gonzalez (jgonzalez) @@ -1506,10 +1588,10 @@ Symfony is the result of the work of many people who made the code better - JuntaTom (juntatom) - Ismail Faizi (kanafghan) - Sébastien Armand (khepin) + - Pierre-Chanel Gauthier (kmecnin) - Krzysztof Menżyk (krymen) - samuel laulhau (lalop) - Laurent Bachelier (laurentb) - - Jérôme Parmentier (lctrs) - Florent Viel (luxifer) - Matthieu Moquet (mattketmo) - Moritz Borgmann (mborgmann) @@ -1531,10 +1613,10 @@ Symfony is the result of the work of many people who made the code better - Daniel Perez Pinazo (pitiflautico) - Brayden Williams (redstar504) - Rich Sage (richsage) - - Ruud Kamphuis (ruudk) - Bart Ruysseveldt (ruyss) - Sascha Dens (saschadens) - scourgen hung (scourgen) + - Sébastien Alfaiate (seb33300) - Sebastian Busch (sebu) - André Filipe Gonçalves Neves (seven) - Bruno Ziegler (sfcoder) @@ -1561,6 +1643,7 @@ Symfony is the result of the work of many people who made the code better - simpson - drublic - Andreas Streichardt + - Pascal Hofmann - smokeybear87 - Gustavo Adrian - Kevin Weber @@ -1583,7 +1666,6 @@ Symfony is the result of the work of many people who made the code better - Muharrem Demirci (mdemirci) - Evgeny Z (meze) - Nicolas de Marqué (nicola) - - Kevin (oxfouzer) - Pierre Geyer (ptheg) - Sam Fleming (sam_fleming) - Thomas BERTRAND (sevrahk) diff --git a/LICENSE b/LICENSE index 12a74531e40a4..17d16a13367dd 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2016 Fabien Potencier +Copyright (c) 2004-2017 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index ecd3b61781c8b..16a7e1b489c4d 100644 --- a/README.md +++ b/README.md @@ -1,48 +1,74 @@ -README -====== +

+ +

-What is Symfony? ------------------ - -Symfony is a PHP full-stack web framework. It is written with speed and -flexibility in mind. It allows developers to build better and easy to maintain -websites with PHP. - -Symfony can be used to develop all kind of websites, from your personal blog -to high traffic ones like Dailymotion or Yahoo! Answers. +[Symfony][1] is a **PHP framework** for web applications and a set of reusable +**PHP components**. Symfony is used by thousands of web applications (including +BlaBlaCar.com and Spotify.com) and most of the [popular PHP projects][2] (including +Drupal and Magento). Installation ------------ -The best way to install Symfony is to use the [official Symfony Installer][7]. -It allows you to start a new project based on the version you want. +* [Install Symfony][4] with Composer or with our own installer (see + [requirements details][3]). +* Symfony follows the [semantic versioning][5] strictly, publishes "Long Term + Support" (LTS) versions and has a [release process][6] that is predictable and + business-friendly. Documentation ------------- -The "[Quick Tour][1]" tutorial gives you a first feeling of the framework. If, -like us, you think that Symfony can help speed up your development and take -the quality of your work to the next level, read the official -[Symfony documentation][2]. +* Read the [Getting Started guide][7] if you are new to Symfony. +* Try the [Symfony Demo application][23] to learn Symfony in practice. +* Master Symfony with the [Guides and Tutorials][8], the [Components docs][9] + and the [Best Practices][10] reference. + +Community +--------- + +* [Join the Symfony Community][11] and meet other members at the [Symfony events][12]. +* [Get Symfony support][13] on Stack Overflow, Slack, IRC, etc. +* Follow us on [GitHub][14], [Twitter][15] and [Facebook][16]. Contributing ------------ -Symfony is an open source, community-driven project. If you'd like to contribute, -please read the [Contributing Code][3] part of the documentation. If you're submitting -a pull request, please follow the guidelines in the [Submitting a Patch][4] section -and use [Pull Request Template][5]. +Symfony is an Open Source, community-driven project with thousands of +[contributors][19]. Join them [contributing code][17] or [contributing documentation][18]. + +Security Issues +--------------- + +If you discover a security vulnerability within Symfony, please follow our +[disclosure procedure][20]. -Running Symfony Tests ----------------------- +About Us +-------- -Information on how to run the Symfony test suite can be found in the -[Running Symfony Tests][6] section. +Symfony development is sponsored by [SensioLabs][21], led by the +[Symfony Core Team][22] and supported by [Symfony contributors][19]. -[1]: https://symfony.com/doc/current/quick_tour/index.html -[2]: https://symfony.com/doc/current/ -[3]: https://symfony.com/doc/current/contributing/code/index.html -[4]: https://symfony.com/doc/current/contributing/code/patches.html#check-list -[5]: https://symfony.com/doc/current/contributing/code/patches.html#make-a-pull-request -[6]: https://symfony.com/doc/master/contributing/code/tests.html -[7]: https://symfony.com/doc/current/book/installation.html#installing-the-symfony-installer +[1]: https://symfony.com +[2]: https://symfony.com/projects +[3]: https://symfony.com/doc/current/reference/requirements.html +[4]: https://symfony.com/doc/current/setup.html +[5]: http://semver.org +[6]: https://symfony.com/doc/current/contributing/community/releases.html +[7]: https://symfony.com/doc/current/page_creation.html +[8]: https://symfony.com/doc/current/index.html +[9]: https://symfony.com/doc/current/components/index.html +[10]: https://symfony.com/doc/current/best_practices/index.html +[11]: https://symfony.com/community +[12]: https://symfony.com/events/ +[13]: https://symfony.com/support +[14]: https://github.com/symfony +[15]: https://twitter.com/symfony +[16]: https://www.facebook.com/SymfonyFramework/ +[17]: https://symfony.com/doc/current/contributing/code/index.html +[18]: https://symfony.com/doc/current/contributing/documentation/index.html +[19]: https://symfony.com/contributors +[20]: https://symfony.com/security +[21]: https://sensiolabs.com +[22]: https://symfony.com/doc/current/contributing/code/core_team.html +[23]: https://github.com/symfony/symfony-demo diff --git a/UPGRADE-3.0.md b/UPGRADE-3.0.md index 866bd981cce79..dcf514f9f84fc 100644 --- a/UPGRADE-3.0.md +++ b/UPGRADE-3.0.md @@ -1144,7 +1144,7 @@ UPGRADE FROM 2.x to 3.0 'http_digest' => array('secret' => '%secret%'), ), )); - ``` + ``` * The `AbstractVoter` class was removed. Instead, extend the new `Voter` class, introduced in 2.8, and move your voting logic to the to the `supports($attribute, $subject)` @@ -1907,3 +1907,9 @@ UPGRADE FROM 2.x to 3.0 ```php $request->query->get('foo')['bar']; ``` +### Monolog Bridge + + * `Symfony\Bridge\Monolog\Logger::emerg()` was removed. Use `emergency()` which is PSR-3 compatible. + * `Symfony\Bridge\Monolog\Logger::crit()` was removed. Use `critical()` which is PSR-3 compatible. + * `Symfony\Bridge\Monolog\Logger::err()` was removed. Use `error()` which is PSR-3 compatible. + * `Symfony\Bridge\Monolog\Logger::warn()` was removed. Use `warning()` which is PSR-3 compatible. diff --git a/UPGRADE-3.1.md b/UPGRADE-3.1.md index b9163cfff369e..58ce838cfb714 100644 --- a/UPGRADE-3.1.md +++ b/UPGRADE-3.1.md @@ -111,6 +111,10 @@ Serializer deprecated and will not be supported in Symfony 4.0. You should use the `CacheClassMetadataFactory` class instead. + * The `AbstractObjectNormalizer::isAttributeToNormalize()` method has been removed + because it was initially added by mistake, has never been used and is not tested + nor documented. + Translation ----------- diff --git a/UPGRADE-3.2.md b/UPGRADE-3.2.md index f4b043c754c66..e151275472755 100644 --- a/UPGRADE-3.2.md +++ b/UPGRADE-3.2.md @@ -6,34 +6,58 @@ BrowserKit * Client HTTP user agent has been changed to 'Symfony BrowserKit' (was 'Symfony2 BrowserKit' before). -FrameworkBundle ---------------- - - * The `doctrine/annotations` dependency has been removed; require it via `composer - require doctrine/annotations` if you are using annotations in your project - * The `symfony/security-core` and `symfony/security-csrf` dependencies have - been removed; require them via `composer require symfony/security-core - symfony/security-csrf` if you depend on them and don't already depend on - `symfony/symfony` - * The `symfony/templating` dependency has been removed; require it via `composer - require symfony/templating` if you depend on it and don't already depend on - `symfony/symfony` - * The `symfony/translation` dependency has been removed; require it via `composer - require symfony/translation` if you depend on it and don't already depend on - `symfony/symfony` - * The `symfony/asset` dependency has been removed; require it via `composer - require symfony/asset` if you depend on it and don't already depend on - `symfony/symfony` - * The `Resources/public/images/*` files have been removed. - * The `Resources/public/css/*.css` files have been removed (they are now inlined - in TwigBundle). - Console ------- * Setting unknown style options is deprecated and will throw an exception in Symfony 4.0. + * The `QuestionHelper::setInputStream()` method is deprecated and will be + removed in Symfony 4.0. Use `StreamableInputInterface::setStream()` or + `CommandTester::setInputs()` instead. + + Before: + + ```php + $input = new ArrayInput(); + + $questionHelper->setInputStream($stream); + $questionHelper->ask($input, $output, $question); + ``` + + After: + + ```php + $input = new ArrayInput(); + $input->setStream($stream); + + $questionHelper->ask($input, $output, $question); + ``` + + Before: + + ```php + $commandTester = new CommandTester($command); + + $stream = fopen('php://memory', 'r+', false); + fputs($stream, "AppBundle\nYes"); + rewind($stream); + + $command->getHelper('question')->setInputStream($stream); + + $commandTester->execute(); + ``` + + After: + + ```php + $commandTester = new CommandTester($command); + + $commandTester->setInputs(array('AppBundle', 'Yes')); + + $commandTester->execute(); + ``` + DependencyInjection ------------------- @@ -43,9 +67,9 @@ DependencyInjection ExpressionLanguage ------------------- -* Passing a `ParserCacheInterface` instance to the `ExpressionLanguage` has been - deprecated and will not be supported in Symfony 4.0. You should use the - `CacheItemPoolInterface` interface instead. + * Passing a `ParserCacheInterface` instance to the `ExpressionLanguage` has been + deprecated and will not be supported in Symfony 4.0. You should use the + `CacheItemPoolInterface` interface instead. Form ---- @@ -72,27 +96,27 @@ Form FrameworkBundle --------------- + * The `doctrine/annotations` dependency has been removed; require it via `composer + require doctrine/annotations` if you are using annotations in your project + * The `symfony/security-core` and `symfony/security-csrf` dependencies have + been removed; require them via `composer require symfony/security-core + symfony/security-csrf` if you depend on them and don't already depend on + `symfony/symfony` + * The `symfony/templating` dependency has been removed; require it via `composer + require symfony/templating` if you depend on it and don't already depend on + `symfony/symfony` + * The `symfony/translation` dependency has been removed; require it via `composer + require symfony/translation` if you depend on it and don't already depend on + `symfony/symfony` + * The `symfony/asset` dependency has been removed; require it via `composer + require symfony/asset` if you depend on it and don't already depend on + `symfony/symfony` + * The `Resources/public/images/*` files have been removed. + * The `Resources/public/css/*.css` files have been removed (they are now inlined + in TwigBundle). * The service `serializer.mapping.cache.doctrine.apc` is deprecated. APCu should now be automatically used when available. -HttpKernel ----------- - - * `DataCollector::varToString()` is deprecated and will be removed in Symfony - 4.0. Use the `cloneVar()` method instead. - - * Surrogate name in a `Surrogate-Capability` HTTP request header has been changed to 'symfony'. - - Before: - ``` - Surrogate-Capability: symfony2="ESI/1.0" - ``` - - After: - ``` - Surrogate-Capability: symfony="ESI/1.0" - ``` - HttpFoundation --------------- @@ -116,11 +140,75 @@ HttpFoundation - `isInvalid`/`isSuccessful`/`isRedirection`/`isClientError`/`isServerError` - `isOk`/`isForbidden`/`isNotFound`/`isRedirect`/`isEmpty` + * Checking only for cacheable HTTP methods with `Request::isMethodSafe()` is deprecated + since version 3.2 and will throw an exception in 4.0. Disable checking only for + cacheable methods by calling the method with `false` as first argument or use + `Request::isMethodCacheable()` instead. + +HttpKernel +---------- + + * `DataCollector::varToString()` is deprecated and will be removed in Symfony + 4.0. Use the `cloneVar()` method instead. + + * Surrogate name in a `Surrogate-Capability` HTTP request header has been changed to 'symfony'. + + Before: + ``` + Surrogate-Capability: symfony2="ESI/1.0" + ``` + + After: + ``` + Surrogate-Capability: symfony="ESI/1.0" + ``` + +Router +------ + + * `UrlGenerator` now generates URLs in compliance with [`RFC 3986`](https://www.ietf.org/rfc/rfc3986.txt), + which means spaces will be percent encoded (%20) inside query strings. + +Serializer +---------- + + * Method `AbstractNormalizer::instantiateObject()` will have a 6th + `$format = null` argument in Symfony 4.0. Not defining it when overriding + the method is deprecated. + TwigBridge ---------- - * Deprecated the possibility to inject the Form Twig Renderer into the form - extension. Inject it into the `TwigRendererEngine` instead. + * Injecting the Form `TwigRenderer` into the `FormExtension` is deprecated and has no more effect. + Upgrade Twig to `^1.30`, inject the `Twig_Environment` into the `TwigRendererEngine` and load + the `TwigRenderer` using the `Twig_FactoryRuntimeLoader` instead. + + Before: + + ```php + use Symfony\Bridge\Twig\Extension\FormExtension; + use Symfony\Bridge\Twig\Form\TwigRenderer; + use Symfony\Bridge\Twig\Form\TwigRendererEngine; + + // ... + $rendererEngine = new TwigRendererEngine(array('form_div_layout.html.twig')); + $rendererEngine->setEnvironment($twig); + $twig->addExtension(new FormExtension(new TwigRenderer($rendererEngine, $csrfTokenManager))); + ``` + + After: + + ```php + $rendererEngine = new TwigRendererEngine(array('form_div_layout.html.twig'), $twig); + $twig->addRuntimeLoader(new \Twig_FactoryRuntimeLoader(array( + TwigRenderer::class => function () use ($rendererEngine, $csrfTokenManager) { + return new TwigRenderer($rendererEngine, $csrfTokenManager); + }, + ))); + $twig->addExtension(new FormExtension()); + ``` + + * Deprecated the `TwigRendererEngineInterface` interface, it will be removed in 4.0. Validator --------- diff --git a/UPGRADE-3.3.md b/UPGRADE-3.3.md new file mode 100644 index 0000000000000..44044e73582fc --- /dev/null +++ b/UPGRADE-3.3.md @@ -0,0 +1,396 @@ +UPGRADE FROM 3.2 to 3.3 +======================= + +BrowserKit +---------- + + * The request method is dropped from POST to GET when the response + status code is 301. + +ClassLoader +----------- + + * The component is deprecated and will be removed in 4.0. Use Composer instead. + +Console +------- + +* `Input::getOption()` no longer returns the default value for options + with value optional explicitly passed empty. + + For: + + ```php + protected function configure() + { + $this + // ... + ->setName('command') + ->addOption('foo', null, InputOption::VALUE_OPTIONAL, '', 'default') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + var_dump($input->getOption('foo')); + } + ``` + + Before: + + ``` + $ php console.php command + "default" + + $ php console.php command --foo + "default" + + $ php console.php command --foo "" + "default" + + $ php console.php command --foo= + "default" + ``` + + After: + + ``` + $ php console.php command + "default" + + $ php console.php command --foo + NULL + + $ php console.php command --foo "" + "" + + $ php console.php command --foo= + "" + ``` + + * The `console.exception` event and the related `ConsoleExceptionEvent` class + have been deprecated in favor of the `console.error` event and the `ConsoleErrorEvent` + class. The deprecated event and class will be removed in 4.0. + +Debug +----- + + * The `ContextErrorException` class is deprecated. `\ErrorException` will be used instead in 4.0. + +DependencyInjection +------------------- + + * Autowiring services based on the types they implement is deprecated and won't be supported in version 4.0. Rename (or alias) your services to their FQCN id to make them autowirable. + + * [BC BREAK] `_defaults` and `_instanceof` are now reserved service names in Yaml configurations. Please rename any services with that names. + + * [BC BREAK] non-numeric keys in methods and constructors arguments have never been supported and are now forbidden. Please remove them if you happen to have one. + + * Service names that start with an underscore are deprecated in Yaml files and will be reserved in 4.0. Please rename any services with such names. + + * Autowiring-types have been deprecated, use aliases instead. + + Before: + + ```xml + + Doctrine\Common\Annotations\Reader + + ``` + + After: + + ```xml + + + ``` + + * The `Reference` and `Alias` classes do not make service identifiers lowercase anymore. + + * Case insensitivity of service identifiers is deprecated and will be removed in 4.0. + + * Using the `PhpDumper` with an uncompiled `ContainerBuilder` is deprecated and + will not be supported anymore in 4.0. + + * Extending the containers generated by `PhpDumper` is deprecated and won't be + supported in 4.0. + + * The `DefinitionDecorator` class is deprecated and will be removed in 4.0, use + the `ChildDefinition` class instead. + + * The ``strict`` attribute in service arguments has been deprecated and will be removed in 4.0. + The attribute is ignored since 3.0, so you can simply remove it. + +EventDispatcher +--------------- + + * The `ContainerAwareEventDispatcher` class has been deprecated. + Use `EventDispatcher` with closure-proxy injection instead. + +Finder +------ + + * The `ExceptionInterface` has been deprecated and will be removed in 4.0. + +Form +---- + + * Using the "choices" option in ``CountryType``, ``CurrencyType``, ``LanguageType``, + ``LocaleType``, and ``TimezoneType`` without overriding the ``choice_loader`` + option has been deprecated and will be ignored in 4.0. + + Before: + ```php + $builder->add('custom_locales', LocaleType::class, array( + 'choices' => $availableLocales, + )); + ``` + + After: + ```php + $builder->add('custom_locales', LocaleType::class, array( + 'choices' => $availableLocales, + 'choice_loader' => null, + )); + // or + $builder->add('custom_locales', LocaleType::class, array( + 'choice_loader' => new CallbackChoiceLoader(function () { + return $this->getAvailableLocales(); + }), + )); + ``` + +FrameworkBundle +--------------- + + * The `cache:clear` command should always be called with the `--no-warmup` option. + Warmup should be done via the `cache:warmup` command. + + * [BC BREAK] The "framework.trusted_proxies" configuration option and the corresponding "kernel.trusted_proxies" + parameter have been removed. Use the Request::setTrustedProxies() method in your front controller instead. + + * Not defining the `type` option of the `framework.workflows.*` configuration entries is deprecated. + The default value will be `state_machine` in Symfony 4.0. + + * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CompilerDebugDumpPass` has been deprecated. + + * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConsoleCommandPass` has been deprecated. + Use `Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass` instead. + + * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\SerializerPass` class has been + deprecated and will be removed in 4.0. + Use the `Symfony\Component\Serializer\DependencyInjection\SerializerPass` class instead. + + * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FormPass` class has been + deprecated and will be removed in 4.0. Use the `Symfony\Component\Form\DependencyInjection\FormPass` + class instead. + + * The `Symfony\Bundle\FrameworkBundle\EventListener\SessionListener` class has been + deprecated and will be removed in 4.0. Use the `Symfony\Component\HttpKernel\EventListener\SessionListener` + class instead. + + * The `Symfony\Bundle\FrameworkBundle\EventListener\TestSessionListener` class has been + deprecated and will be removed in 4.0. Use the `Symfony\Component\HttpKernel\EventListener\TestSessionListener` + class instead. + + * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ConfigCachePass` class has been + deprecated and will be removed in 4.0. Use `Symfony\Component\Config\DependencyInjection\ConfigCachePass` + class instead. + + * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\PropertyInfoPass` class has been + deprecated and will be removed in 4.0. Use the `Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass` + class instead. + + * The `ConstraintValidatorFactory::$validators` and `$container` properties + have been deprecated and will be removed in 4.0. + + * Extending `ConstraintValidatorFactory` is deprecated and won't be supported in 4.0. + + * Class parameters related to routing have been deprecated and will be removed in 4.0. + * router.options.generator_class + * router.options.generator_base_class + * router.options.generator_dumper_class + * router.options.matcher_class + * router.options.matcher_base_class + * router.options.matcher_dumper_class + * router.options.matcher.cache_class + * router.options.generator.cache_class + + * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ControllerArgumentValueResolverPass` class + has been deprecated and will be removed in 4.0. Use the `Symfony\Component\HttpKernel\DependencyInjection\ControllerArgumentValueResolverPass` + class instead. + + * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RoutingResolverPass` + class has been deprecated and will be removed in 4.0. Use the + `Symfony\Component\Routing\DependencyInjection\RoutingResolverPass` class instead. + + * The `server:run`, `server:start`, `server:stop` and + `server:status` console commands have been moved to a dedicated bundle. + Require `symfony/web-server-bundle` in your composer.json and register + `Symfony\Bundle\WebServerBundle\WebServerBundle` in your AppKernel to use them. + + * The `Symfony\Bundle\FrameworkBundle\Translation\Translator` constructor now takes the + default locale as 3rd argument. Not passing it will trigger an error in 4.0. + + * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddValidatorInitializersPass` + class has been deprecated and will be removed in 4.0. + Use the `Symfony\Component\Validator\DependencyInjection\AddValidatorInitializersPass` class instead. + + * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConstraintValidatorsPass` + class has been deprecated and will be removed in 4.0. + Use the `Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass` class instead. + + * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ValidateWorkflowsPass` + class has been deprecated and will be removed in 4.0. Use the + `Symfony\Component\Workflow\DependencyInjection\ValidateWorkflowsPass` class instead. + +HttpFoundation +-------------- + + * [BC BREAK] The `Request::setTrustedProxies()` method takes a new `$trustedHeaderSet` argument. + See http://symfony.com/doc/current/components/http_foundation/trusting_proxies.html for more info. + + * The `Request::setTrustedHeaderName()` and `Request::getTrustedHeaderName()` methods are deprecated, + use the RFC7239 `Forwarded` header, or the `X-Forwarded-*` headers instead. + +HttpKernel +----------- + + * Deprecated the `kernel.root_dir` parameter. Use the new `kernel.project_dir` + parameter instead. + + * Deprecated the `Kernel::getRootDir()` method. Use the new `Kernel::getProjectDir()` + method instead. + + * The `Extension::addClassesToCompile()` method has been deprecated and will be removed in 4.0. + + * The `Psr6CacheClearer::addPool()` method has been deprecated. Pass an array + of pools indexed by name to the constructor instead. + + * The `LazyLoadingFragmentHandler::addRendererService()` method has been + deprecated and will be removed in 4.0. + + * The `X-Status-Code` header method of setting a custom status code in the + response when handling exceptions has been removed. There is now a new + `GetResponseForExceptionEvent::allowCustomResponseCode()` method instead, + which will tell the Kernel to use the response code set on the event's + response object. + + * The `Kernel::getEnvParameters()` method has been deprecated and will be + removed in 4.0. + + * The `SYMFONY__` environment variables have been deprecated and they will be + no longer processed automatically by Symfony in 4.0. Use the `%env()%` syntax + to get the value of any environment variable from configuration files instead. + +Process +------- + + * The `ProcessUtils::escapeArgument()` method has been deprecated, use a command line array or give env vars to the `Process::start/run()` method instead. + + * Not inheriting environment variables is deprecated. + + * Configuring `proc_open()` options is deprecated. + + * Configuring Windows and sigchild compatibility is deprecated - they will be always enabled in 4.0. + + * Extending `Process::run()`, `Process::mustRun()` and `Process::restart()` is + deprecated and won't be supported in 4.0. + +Security +-------- + + * The `RoleInterface` has been deprecated. Extend the `Symfony\Component\Security\Core\Role\Role` + class in your custom role implementations instead. + + * The `LogoutUrlGenerator::registerListener()` method will expect a 6th `$context = null` argument in 4.0. + Define the argument when overriding this method. + + * The `AccessDecisionManager::setVoters()` method has been deprecated. Pass + the voters to the constructor instead. + +SecurityBundle +-------------- + + * The `FirewallContext::getContext()` method has been deprecated and will be removed in 4.0. + Use the `getListeners()` and/or `getExceptionListener()` method instead. + + * The `FirewallMap::$map` and `$container` properties have been deprecated and will be removed in 4.0. + + * The `UserPasswordEncoderCommand` command expects to be registered as a service and its + constructor arguments fully provided. + Registering by convention the command or commands extending it is deprecated and will + not be allowed anymore in 4.0. + + * `UserPasswordEncoderCommand::getContainer()` is deprecated, and this class won't + extend `ContainerAwareCommand` nor implement `ContainerAwareInterface` anymore in 4.0. + + * [BC BREAK] Keys of the `users` node for `in_memory` user provider are no longer normalized. + +Serializer +---------- + + * Extending `ChainDecoder`, `ChainEncoder`, `ArrayDenormalizer` is deprecated + and won't be supported in 4.0. + +TwigBridge +---------- + + * The `TwigRendererEngine::setEnvironment()` method has been deprecated and will be removed + in 4.0. Pass the Twig Environment as second argument of the constructor instead. + +TwigBundle +---------- + +* The `ContainerAwareRuntimeLoader` class has been deprecated and will be removed in 4.0. + Use the Twig `Twig_ContainerRuntimeLoader` class instead. + +Workflow +-------- + + * Deprecated class name support in `WorkflowRegistry::add()` as second parameter. + Wrap the class name in an instance of ClassInstanceSupportStrategy instead. + +Yaml +---- + + * Starting an unquoted string with a question mark followed by a space is + deprecated and will throw a `ParseException` in Symfony 4.0. + + * Deprecated support for implicitly parsing non-string mapping keys as strings. + Mapping keys that are no strings will lead to a `ParseException` in Symfony + 4.0. Use the `PARSE_KEYS_AS_STRINGS` flag to opt-in for keys to be parsed as + strings. + + Before: + + ```php + $yaml = <<setInputStream($stream); + $questionHelper->ask($input, $output, $question); + ``` + + After: + + ```php + $input = new ArrayInput(); + $input->setStream($stream); + + $questionHelper->ask($input, $output, $question); + ``` + + Before: + + ```php + $commandTester = new CommandTester($command); + + $stream = fopen('php://memory', 'r+', false); + fputs($stream, "AppBundle\nYes"); + rewind($stream); + + $command->getHelper('question')->setInputStream($stream); + + $commandTester->execute(); + ``` + + After: + + ```php + $commandTester = new CommandTester($command); + + $commandTester->setInputs(array('AppBundle', 'Yes')); + + $commandTester->execute(); + ``` + + * The `console.exception` event and the related `ConsoleExceptionEvent` class have + been removed in favor of the `console.error` event and the `ConsoleErrorEvent` class. + Debug ----- + + * The `ContextErrorException` class has been removed. Use `\ErrorException` instead. + * `FlattenException::getTrace()` now returns additional type descriptions `integer` and `float`. DependencyInjection ------------------- + * Autowiring services based on the types they implement is not supported anymore. Rename (or alias) your services to their FQCN id to make them autowirable. + + * `_defaults` and `_instanceof` are now reserved service names in Yaml configurations. Please rename any services with that names. + + * Non-numeric keys in methods and constructors arguments have never been supported and are now forbidden. Please remove them if you happen to have one. + + * Service names that start with an underscore are now reserved in Yaml files. Please rename any services with such names. + + * Autowiring-types have been removed, use aliases instead. + + Before: + + ```xml + + Doctrine\Common\Annotations\Reader + + ``` + + After: + + ```xml + + + ``` + + * Service identifiers are now case sensitive. + + * The `Reference` and `Alias` classes do not make service identifiers lowercase anymore. + + * Using the `PhpDumper` with an uncompiled `ContainerBuilder` is not supported + anymore. + + * Extending the containers generated by `PhpDumper` is not supported + anymore. + + * The `DefinitionDecorator` class has been removed. Use the `ChildDefinition` + class instead. + * Using unsupported configuration keys in YAML configuration files raises an exception. @@ -30,13 +125,27 @@ DependencyInjection * Requesting a private service with the `Container::get()` method is no longer supported. + * The ``strict`` attribute in service arguments has been removed. + The attribute is ignored since 3.0, so you can simply remove it. + +EventDispatcher +--------------- + + * The `ContainerAwareEventDispatcher` class has been removed. + Use `EventDispatcher` with closure-proxy injection instead. + ExpressionLanguage ----------- +------------------ * The ability to pass a `ParserCacheInterface` instance to the `ExpressionLanguage` class has been removed. You should use the `CacheItemPoolInterface` interface instead. +Finder +------ + + * The `ExceptionInterface` has been removed. + Form ---- @@ -86,9 +195,41 @@ Form } ``` + * Using the "choices" option in ``CountryType``, ``CurrencyType``, ``LanguageType``, + ``LocaleType``, and ``TimezoneType`` without overriding the ``choice_loader`` + option is now ignored. + + Before: + ```php + $builder->add('custom_locales', LocaleType::class, array( + 'choices' => $availableLocales, + )); + ``` + + After: + ```php + $builder->add('custom_locales', LocaleType::class, array( + 'choices' => $availableLocales, + 'choice_loader' => null, + )); + // or + $builder->add('custom_locales', LocaleType::class, array( + 'choice_loader' => new CallbackChoiceLoader(function () { + return $this->getAvailableLocales(); + }), + )); + ``` + FrameworkBundle --------------- + * The `cache:clear` command does not warmup the cache anymore. Warmup should + be done via the `cache:warmup` command. + + * The "framework.trusted_proxies" configuration option and the corresponding "kernel.trusted_proxies" parameter have been removed. Use the `Request::setTrustedProxies()` method in your front controller instead. + + * The default value of the `framework.workflows.[name].type` configuration options is now `state_machine`. + * Support for absolute template paths has been removed. * The following form types registered as services have been removed; use their @@ -128,8 +269,76 @@ FrameworkBundle `serializer.mapping.cache.apc` and `serializer.mapping.cache.doctrine.apc` have been removed. APCu should now be automatically used when available. + * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CompilerDebugDumpPass` has been removed. + + * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConsoleCommandPass` has been removed. + Use `Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass` instead. + + * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\SerializerPass` class has been removed. + Use the `Symfony\Component\Serializer\DependencyInjection\SerializerPass` class instead. + + * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FormPass` class has been + removed. Use the `Symfony\Component\Form\DependencyInjection\FormPass` class instead. + + * The `Symfony\Bundle\FrameworkBundle\EventListener\SessionListener` class has been removed. + Use the `Symfony\Component\HttpKernel\EventListener\SessionListener` class instead. + + * The `Symfony\Bundle\FrameworkBundle\EventListener\TestSessionListener` class has been + removed. Use the `Symfony\Component\HttpKernel\EventListener\TestSessionListener` + class instead. + + * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ConfigCachePass` class has been removed. + Use `Symfony\Component\Config\DependencyInjection\ConfigCachePass` class instead. + + * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\PropertyInfoPass` class has been + removed. Use the `Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass` + class instead. + + * The `ConstraintValidatorFactory::$validators` and `$container` properties + have been removed. + + * Extending `ConstraintValidatorFactory` is not supported anymore. + + * Class parameters related to routing have been removed + * router.options.generator_class + * router.options.generator_base_class + * router.options.generator_dumper_class + * router.options.matcher_class + * router.options.matcher_base_class + * router.options.matcher_dumper_class + * router.options.matcher.cache_class + * router.options.generator.cache_class + + * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ControllerArgumentValueResolverPass` class + has been removed. Use the `Symfony\Component\HttpKernel\DependencyInjection\ControllerArgumentValueResolverPass` + class instead. + + * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RoutingResolverPass` + class has been removed. Use the + `Symfony\Component\Routing\DependencyInjection\RoutingResolverPass` class instead. + + * The `Symfony\Bundle\FrameworkBundle\Translation\Translator` constructor now takes the + default locale as mandatory 3rd argument. + + * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddValidatorInitializersPass` class has been + removed. Use the `Symfony\Component\Validator\DependencyInjection\AddValidatorInitializersPass` + class instead. + + * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConstraintValidatorsPass` class has been + removed. Use the `Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass` + class instead. + + * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ValidateWorkflowsPass` class + has been removed. Use the `Symfony\Component\Workflow\DependencyInjection\ValidateWorkflowsPass` + class instead. + HttpFoundation ---------------- +-------------- + + * The `Request::setTrustedProxies()` method takes a new `$trustedHeaderSet` argument. + See http://symfony.com/doc/current/components/http_foundation/trusting_proxies.html for more info. + + * The `Request::setTrustedHeaderName()` and `Request::getTrustedHeaderName()` methods have been removed. * Extending the following methods of `Response` is no longer possible (these methods are now `final`): @@ -151,9 +360,20 @@ HttpFoundation - `isInvalid`/`isSuccessful`/`isRedirection`/`isClientError`/`isServerError` - `isOk`/`isForbidden`/`isNotFound`/`isRedirect`/`isEmpty` + * The ability to check only for cacheable HTTP methods using `Request::isMethodSafe()` is + not supported anymore, use `Request::isMethodCacheable()` instead. + HttpKernel ---------- + * Removed the `kernel.root_dir` parameter. Use the `kernel.project_dir` parameter + instead. + + * Removed the `Kernel::getRootDir()` method. Use the `Kernel::getProjectDir()` + method instead. + + * The `Extension::addClassesToCompile()` method has been removed. + * Possibility to pass non-scalar values as URI attributes to the ESI and SSI renderers has been removed. The inline fragment renderer should be used with non-scalar attributes. @@ -164,6 +384,64 @@ HttpKernel * The `DataCollector::varToString()` method has been removed in favor of `cloneVar()`. + * The `Psr6CacheClearer::addPool()` method has been removed. Pass an array of pools indexed + by name to the constructor instead. + + * The `LazyLoadingFragmentHandler::addRendererService()` method has been removed. + + * The `X-Status-Code` header method of setting a custom status code in the + response when handling exceptions has been removed. There is now a new + `GetResponseForExceptionEvent::allowCustomResponseCode()` method instead, + which will tell the Kernel to use the response code set on the event's + response object. + + * The `Kernel::getEnvParameters()` method has been removed. + + * The `SYMFONY__` environment variables are no longer processed automatically + by Symfony. Use the `%env()%` syntax to get the value of any environment + variable from configuration files instead. + +Ldap +---- + + * The `RenameEntryInterface` has been deprecated, and merged with `EntryManagerInterface` + +Process +------- + + * The `ProcessUtils::escapeArgument()` method has been removed, use a command line array or give env vars to the `Process::start/run()` method instead. + + * Environment variables are always inherited in sub-processes. + + * Configuring `proc_open()` options has been removed. + + * Configuring Windows and sigchild compatibility is not possible anymore - they are always enabled. + + * Extending `Process::run()`, `Process::mustRun()` and `Process::restart()` is + not supported anymore. + +Security +-------- + + * The `RoleInterface` has been removed. Extend the `Symfony\Component\Security\Core\Role\Role` + class instead. + + * The `LogoutUrlGenerator::registerListener()` method expects a 6th `$context = null` argument. + + * The `AccessDecisionManager::setVoters()` method has been removed. Pass the + voters to the constructor instead. + +SecurityBundle +-------------- + + * The `FirewallContext::getContext()` method has been removed, use the `getListeners()` and/or `getExceptionListener()` method instead. + + * The `FirewallMap::$map` and `$container` properties have been removed. + + * The `UserPasswordEncoderCommand` class does not allow `null` as the first argument anymore. + + * `UserPasswordEncoderCommand` does not implement `ContainerAwareInterface` anymore. + Serializer ---------- @@ -171,20 +449,141 @@ Serializer class has been removed. You should use the `CacheClassMetadataFactory` class instead. + * Not defining the 6th argument `$format = null` of the + `AbstractNormalizer::instantiateObject()` method when overriding it is not + supported anymore. + + * Extending `ChainDecoder`, `ChainEncoder`, `ArrayDenormalizer` is not supported + anymore. + Translation ----------- * Removed the backup feature from the file dumper classes. +TwigBundle +---------- + +* The `ContainerAwareRuntimeLoader` class has been removed. Use the + Twig `Twig_ContainerRuntimeLoader` class instead. + TwigBridge ---------- - * The possibility to inject the Form Twig Renderer into the form extension - has been removed. Inject it into the `TwigRendererEngine` instead. + * Removed the possibility to inject the Form `TwigRenderer` into the `FormExtension`. + Upgrade Twig to `^1.30`, inject the `Twig_Environment` into the `TwigRendererEngine` and load + the `TwigRenderer` using the `Twig_FactoryRuntimeLoader` instead. + + Before: + + ```php + use Symfony\Bridge\Twig\Extension\FormExtension; + use Symfony\Bridge\Twig\Form\TwigRenderer; + use Symfony\Bridge\Twig\Form\TwigRendererEngine; + + // ... + $rendererEngine = new TwigRendererEngine(array('form_div_layout.html.twig')); + $rendererEngine->setEnvironment($twig); + $twig->addExtension(new FormExtension(new TwigRenderer($rendererEngine, $csrfTokenManager))); + ``` + + After: + + ```php + $rendererEngine = new TwigRendererEngine(array('form_div_layout.html.twig'), $twig); + $twig->addRuntimeLoader(new \Twig_FactoryRuntimeLoader(array( + TwigRenderer::class => function () use ($rendererEngine, $csrfTokenManager) { + return new TwigRenderer($rendererEngine, $csrfTokenManager); + }, + ))); + $twig->addExtension(new FormExtension()); + ``` + + * Removed the `TwigRendererEngineInterface` interface. + + * The `TwigRendererEngine::setEnvironment()` method has been removed. + Pass the Twig Environment as second argument of the constructor instead. + +Validator +--------- + + * The `DateTimeValidator::PATTERN` constant was removed. + + * `Tests\Constraints\AbstractConstraintValidatorTest` has been removed in + favor of `Test\ConstraintValidatorTestCase`. + + Before: + + ```php + // ... + use Symfony\Component\Validator\Tests\Constraints\AbstractConstraintValidatorTest; + + class MyCustomValidatorTest extends AbstractConstraintValidatorTest + { + // ... + } + ``` + + After: + + ```php + // ... + use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; + + class MyCustomValidatorTest extends ConstraintValidatorTestCase + { + // ... + } + ``` + + * The default value of the strict option of the `Choice` Constraint has been + changed to `true` as of 4.0. If you need the previous behaviour ensure to + set the option to `false`. + +Workflow +-------- + + * Removed class name support in `WorkflowRegistry::add()` as second parameter. Yaml ---- + * Starting an unquoted string with a question mark followed by a space + throws a `ParseException`. + + * Removed support for implicitly parsing non-string mapping keys as strings. + Mapping keys that are no strings will result in a `ParseException`. Use the + `PARSE_KEYS_AS_STRINGS` flag to opt-in for keys to be parsed as strings. + + Before: + + ```php + $yaml = << phpunit init: @@ -46,13 +46,12 @@ install: - echo curl.cainfo=c:\php\cacert.pem >> php.ini-max - copy /Y php.ini-max php.ini - cd c:\projects\symfony - - IF NOT EXIST composer.phar (appveyor DownloadFile https://getcomposer.org/download/1.2.1/composer.phar) + - IF NOT EXIST composer.phar (appveyor DownloadFile https://getcomposer.org/download/1.3.0/composer.phar) - php composer.phar self-update - copy /Y .composer\* %APPDATA%\Composer\ - php .github/build-packages.php "HEAD^" src\Symfony\Bridge\PhpUnit - IF %APPVEYOR_REPO_BRANCH%==master (SET COMPOSER_ROOT_VERSION=dev-master) ELSE (SET COMPOSER_ROOT_VERSION=%APPVEYOR_REPO_BRANCH%.x-dev) - - php composer.phar update --no-progress --ansi - - SET COMPOSER_ROOT_VERSION= + - php composer.phar update --no-progress --no-suggest --ansi - php phpunit install test_script: diff --git a/composer.json b/composer.json index 9677a86460fbe..13ad97446582a 100644 --- a/composer.json +++ b/composer.json @@ -18,9 +18,13 @@ "require": { "php": ">=5.5.9", "doctrine/common": "~2.4", - "twig/twig": "~1.28|~2.0", + "fig/link-util": "^1.0", + "twig/twig": "~1.32|~2.2", "psr/cache": "~1.0", + "psr/container": "^1.0", + "psr/link": "^1.0", "psr/log": "~1.0", + "psr/simple-cache": "^1.0", "symfony/polyfill-intl-icu": "~1.0", "symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-php56": "~1.0", @@ -40,6 +44,7 @@ "symfony/debug-bundle": "self.version", "symfony/doctrine-bridge": "self.version", "symfony/dom-crawler": "self.version", + "symfony/dotenv": "self.version", "symfony/event-dispatcher": "self.version", "symfony/expression-language": "self.version", "symfony/filesystem": "self.version", @@ -72,7 +77,9 @@ "symfony/twig-bundle": "self.version", "symfony/validator": "self.version", "symfony/var-dumper": "self.version", + "symfony/web-link": "self.version", "symfony/web-profiler-bundle": "self.version", + "symfony/web-server-bundle": "self.version", "symfony/workflow": "self.version", "symfony/yaml": "self.version" }, @@ -90,14 +97,18 @@ "symfony/phpunit-bridge": "~3.2", "symfony/polyfill-apcu": "~1.1", "symfony/security-acl": "~2.8|~3.0", - "phpdocumentor/reflection-docblock": "^3.0" + "phpdocumentor/reflection-docblock": "^3.0", + "sensio/framework-extra-bundle": "^3.0.2" }, "conflict": { "phpdocumentor/reflection-docblock": "<3.0", - "phpdocumentor/type-resolver": "<0.2.0" + "phpdocumentor/type-resolver": "<0.2.0", + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" }, "provide": { - "psr/cache-implementation": "1.0" + "psr/cache-implementation": "1.0", + "psr/container-implementation": "1.0", + "psr/simple-cache-implementation": "1.0" }, "autoload": { "psr-4": { @@ -116,10 +127,13 @@ "**/Tests/" ] }, + "autoload-dev": { + "files": [ "src/Symfony/Component/VarDumper/Resources/functions/dump.php" ] + }, "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.3-dev" } } } diff --git a/phpunit b/phpunit index f9243bcbf9e79..466f8fbbc2356 100755 --- a/phpunit +++ b/phpunit @@ -1,9 +1,14 @@ #!/usr/bin/env php = 70000 && !getenv('SYMFONY_PHPUNIT_VERSION')) { + putenv('SYMFONY_PHPUNIT_VERSION=6.0'); +} putenv('SYMFONY_PHPUNIT_DIR='.__DIR__.'/.phpunit'); require __DIR__.'/vendor/symfony/phpunit-bridge/bin/simple-phpunit'; diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 7652c58475569..e44535a81a19e 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -5,6 +5,8 @@ backupGlobals="false" colors="true" bootstrap="vendor/autoload.php" + failOnRisky="true" + failOnWarning="true" > @@ -16,6 +18,7 @@ + @@ -61,6 +64,8 @@ Cache\IntegrationTests Doctrine\Common\Cache + Symfony\Component\Cache + Symfony\Component\Cache\Traits Symfony\Component\Console Symfony\Component\HttpFoundation diff --git a/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php b/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php index 0888f97ca91d8..941d77cf591a2 100644 --- a/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php +++ b/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php @@ -13,7 +13,7 @@ use Doctrine\Common\EventArgs; use Doctrine\Common\EventManager; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Psr\Container\ContainerInterface; /** * Allows lazy loading of listener services. diff --git a/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php b/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php index a57b9ae6ea151..62c6a2381a940 100644 --- a/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php +++ b/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php @@ -159,7 +159,11 @@ private function sanitizeQuery($connectionName, $query) private function sanitizeParam($var) { if (is_object($var)) { - return array(sprintf('Object(%s)', get_class($var)), false); + $className = get_class($var); + + return method_exists($var, '__toString') ? + array(sprintf('Object(%s): "%s"', $className, $var->__toString()), false) : + array(sprintf('Object(%s)', $className), false); } if (is_array($var)) { diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php index b9cac9f71845a..37875b53b570a 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php @@ -16,7 +16,6 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\Config\Resource\FileResource; /** * This abstract classes groups common code that Doctrine Object Manager extensions (ORM, MongoDB, CouchDB) need. @@ -268,30 +267,28 @@ protected function assertValidMappingConfiguration(array $mappingConfig, $object */ protected function detectMetadataDriver($dir, ContainerBuilder $container) { - // add the closest existing directory as a resource $configPath = $this->getMappingResourceConfigDirectory(); - $resource = $dir.'/'.$configPath; - while (!is_dir($resource)) { - $resource = dirname($resource); - } - - $container->addResource(new FileResource($resource)); - $extension = $this->getMappingResourceExtension(); - if (($files = glob($dir.'/'.$configPath.'/*.'.$extension.'.xml')) && count($files)) { - return 'xml'; - } elseif (($files = glob($dir.'/'.$configPath.'/*.'.$extension.'.yml')) && count($files)) { - return 'yml'; - } elseif (($files = glob($dir.'/'.$configPath.'/*.'.$extension.'.php')) && count($files)) { - return 'php'; - } - // add the directory itself as a resource - $container->addResource(new FileResource($dir)); + if (glob($dir.'/'.$configPath.'/*.'.$extension.'.xml')) { + $driver = 'xml'; + } elseif (glob($dir.'/'.$configPath.'/*.'.$extension.'.yml')) { + $driver = 'yml'; + } elseif (glob($dir.'/'.$configPath.'/*.'.$extension.'.php')) { + $driver = 'php'; + } else { + // add the closest existing directory as a resource + $resource = $dir.'/'.$configPath; + while (!is_dir($resource)) { + $resource = dirname($resource); + } + $container->fileExists($resource, false); - if (is_dir($dir.'/'.$this->getMappingObjectDefaultName())) { - return 'annotation'; + return $container->fileExists($dir.'/'.$this->getMappingObjectDefaultName(), false) ? 'annotation' : null; } + $container->fileExists($dir.'/'.$configPath, false); + + return $driver; } /** diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/DoctrineValidationPass.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/DoctrineValidationPass.php index f8382ed2ebfb8..2a9da54961d39 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/DoctrineValidationPass.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/DoctrineValidationPass.php @@ -13,7 +13,6 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\Config\Resource\FileResource; /** * Registers additional validators. @@ -60,9 +59,8 @@ private function updateValidatorMappingFiles(ContainerBuilder $container, $mappi foreach ($container->getParameter('kernel.bundles') as $bundle) { $reflection = new \ReflectionClass($bundle); - if (is_file($file = dirname($reflection->getFileName()).'/'.$validationPath)) { + if ($container->fileExists($file = dirname($reflection->getFileName()).'/'.$validationPath)) { $files[] = $file; - $container->addResource(new FileResource($file)); } } diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php index 1739dc3aea92c..cac2100794796 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php @@ -55,8 +55,8 @@ public function process(ContainerBuilder $container) return; } - $taggedSubscribers = $container->findTaggedServiceIds($this->tagPrefix.'.event_subscriber'); - $taggedListeners = $container->findTaggedServiceIds($this->tagPrefix.'.event_listener'); + $taggedSubscribers = $container->findTaggedServiceIds($this->tagPrefix.'.event_subscriber', true); + $taggedListeners = $container->findTaggedServiceIds($this->tagPrefix.'.event_listener', true); if (empty($taggedSubscribers) && empty($taggedListeners)) { return; @@ -78,10 +78,6 @@ public function process(ContainerBuilder $container) uasort($subscribers, $sortFunc); foreach ($subscribers as $id => $instance) { - if ($container->getDefinition($id)->isAbstract()) { - throw new InvalidArgumentException(sprintf('The abstract service "%s" cannot be tagged as a doctrine event subscriber.', $id)); - } - $em->addMethodCall('addEventSubscriber', array(new Reference($id))); } } @@ -94,10 +90,6 @@ public function process(ContainerBuilder $container) uasort($listeners, $sortFunc); foreach ($listeners as $id => $instance) { - if ($container->getDefinition($id)->isAbstract()) { - throw new InvalidArgumentException(sprintf('The abstract service "%s" cannot be tagged as a doctrine event listener.', $id)); - } - $em->addMethodCall('addEventListener', array( array_unique($instance['event']), isset($instance['lazy']) && $instance['lazy'] ? $id : new Reference($id), @@ -122,7 +114,7 @@ private function groupByConnection(array $services, $isListener = false) } $instance['event'] = array($instance['event']); - if (isset($instance['lazy']) && $instance['lazy']) { + if ($lazy = !empty($instance['lazy'])) { $this->container->getDefinition($id)->setPublic(true); } } diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/Security/UserProvider/EntityFactory.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/Security/UserProvider/EntityFactory.php index ebcdf82f585a3..49c528ba00fdb 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/Security/UserProvider/EntityFactory.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/Security/UserProvider/EntityFactory.php @@ -13,7 +13,7 @@ use Symfony\Component\Config\Definition\Builder\NodeDefinition; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface; -use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; /** @@ -36,7 +36,7 @@ public function __construct($key, $providerId) public function create(ContainerBuilder $container, $id, $config) { $container - ->setDefinition($id, new DefinitionDecorator($this->providerId)) + ->setDefinition($id, new ChildDefinition($this->providerId)) ->addArgument($config['class']) ->addArgument($config['property']) ->addArgument($config['manager_name']) diff --git a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php index 9477d8265529d..f199f16265fac 100644 --- a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php +++ b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php @@ -61,9 +61,9 @@ class DoctrineChoiceLoader implements ChoiceLoaderInterface * loaded objects * @param IdReader $idReader The reader for the object * IDs. + * @param null|EntityLoaderInterface $objectLoader The objects loader * @param ChoiceListFactoryInterface $factory The factory for creating * the loaded choice list - * @param null|EntityLoaderInterface $objectLoader The objects loader */ public function __construct($manager, $class, $idReader = null, $objectLoader = null, $factory = null) { diff --git a/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmExtension.php b/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmExtension.php index ed8e0a793444c..fe86b103cbb34 100644 --- a/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmExtension.php +++ b/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmExtension.php @@ -14,38 +14,20 @@ use Doctrine\Common\Persistence\ManagerRegistry; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractExtension; -use Symfony\Component\Form\ChoiceList\Factory\CachingFactoryDecorator; -use Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface; -use Symfony\Component\Form\ChoiceList\Factory\DefaultChoiceListFactory; -use Symfony\Component\Form\ChoiceList\Factory\PropertyAccessDecorator; -use Symfony\Component\PropertyAccess\PropertyAccess; -use Symfony\Component\PropertyAccess\PropertyAccessorInterface; class DoctrineOrmExtension extends AbstractExtension { protected $registry; - /** - * @var PropertyAccessorInterface - */ - private $propertyAccessor; - - /** - * @var ChoiceListFactoryInterface - */ - private $choiceListFactory; - - public function __construct(ManagerRegistry $registry, PropertyAccessorInterface $propertyAccessor = null, ChoiceListFactoryInterface $choiceListFactory = null) + public function __construct(ManagerRegistry $registry) { $this->registry = $registry; - $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor(); - $this->choiceListFactory = $choiceListFactory ?: new CachingFactoryDecorator(new PropertyAccessDecorator(new DefaultChoiceListFactory(), $this->propertyAccessor)); } protected function loadTypes() { return array( - new EntityType($this->registry, $this->propertyAccessor, $this->choiceListFactory), + new EntityType($this->registry), ); } diff --git a/src/Symfony/Bridge/Doctrine/LICENSE b/src/Symfony/Bridge/Doctrine/LICENSE index 12a74531e40a4..17d16a13367dd 100644 --- a/src/Symfony/Bridge/Doctrine/LICENSE +++ b/src/Symfony/Bridge/Doctrine/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2016 Fabien Potencier +Copyright (c) 2004-2017 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bridge/Doctrine/ManagerRegistry.php b/src/Symfony/Bridge/Doctrine/ManagerRegistry.php index 3eaf8dbc51188..d602bfc2acaad 100644 --- a/src/Symfony/Bridge/Doctrine/ManagerRegistry.php +++ b/src/Symfony/Bridge/Doctrine/ManagerRegistry.php @@ -45,7 +45,7 @@ protected function resetService($name) $manager = $this->container->get($name); if (!$manager instanceof LazyLoadingInterface) { - @trigger_error(sprintf('Resetting a non-lazy manager service is deprecated since Symfony 3.2 and will throw an exception in version 4.0. Set the "%s" service as lazy and require "symfony/proxy-manager-bridge" in your composer.json file instead.', $name)); + @trigger_error(sprintf('Resetting a non-lazy manager service is deprecated since Symfony 3.2 and will throw an exception in version 4.0. Set the "%s" service as lazy and require "symfony/proxy-manager-bridge" in your composer.json file instead.', $name), E_USER_DEPRECATED); $this->container->set($name, null); diff --git a/src/Symfony/Bridge/Doctrine/Test/DoctrineTestHelper.php b/src/Symfony/Bridge/Doctrine/Test/DoctrineTestHelper.php index e7f4555d07d2e..ee1e36859ed14 100644 --- a/src/Symfony/Bridge/Doctrine/Test/DoctrineTestHelper.php +++ b/src/Symfony/Bridge/Doctrine/Test/DoctrineTestHelper.php @@ -16,6 +16,7 @@ use Doctrine\ORM\Configuration; use Doctrine\ORM\Mapping\Driver\AnnotationDriver; use Doctrine\ORM\EntityManager; +use PHPUnit\Framework\TestCase; /** * Provides utility functions needed in tests. @@ -34,7 +35,7 @@ class DoctrineTestHelper public static function createTestEntityManager(Configuration $config = null) { if (!extension_loaded('pdo_sqlite')) { - \PHPUnit_Framework_TestCase::markTestSkipped('Extension pdo_sqlite is required.'); + TestCase::markTestSkipped('Extension pdo_sqlite is required.'); } if (null === $config) { diff --git a/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php b/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php index 55991dbf4f653..f40d1ca710ed1 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php @@ -11,10 +11,11 @@ namespace Symfony\Bridge\Doctrine\Tests; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\ContainerAwareEventManager; use Symfony\Component\DependencyInjection\Container; -class ContainerAwareEventManagerTest extends \PHPUnit_Framework_TestCase +class ContainerAwareEventManagerTest extends TestCase { private $container; private $evm; diff --git a/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php b/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php index 45d0310e6dac8..782c39431cd66 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php @@ -12,11 +12,12 @@ namespace Symfony\Bridge\Doctrine\Tests\DataCollector; use Doctrine\DBAL\Platforms\MySqlPlatform; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\DataCollector\DoctrineDataCollector; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -class DoctrineDataCollectorTest extends \PHPUnit_Framework_TestCase +class DoctrineDataCollectorTest extends TestCase { public function testCollectConnections() { @@ -126,7 +127,13 @@ public function paramProvider() array(null, array(), null, true), array(new \DateTime('2011-09-11'), array('date'), '2011-09-11', true), array(fopen(__FILE__, 'r'), array(), 'Resource(stream)', false), - array(new \SplFileInfo(__FILE__), array(), 'Object(SplFileInfo)', false), + array(new \stdClass(), array(), 'Object(stdClass)', false), + array( + new StringRepresentableClass(), + array(), + 'Object(Symfony\Bridge\Doctrine\Tests\DataCollector\StringRepresentableClass): "string representation"', + false, + ), ); } @@ -139,7 +146,7 @@ private function createCollector($queries) ->method('getDatabasePlatform') ->will($this->returnValue(new MySqlPlatform())); - $registry = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry'); + $registry = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')->getMock(); $registry ->expects($this->any()) ->method('getConnectionNames') @@ -152,7 +159,7 @@ private function createCollector($queries) ->method('getConnection') ->will($this->returnValue($connection)); - $logger = $this->getMock('Doctrine\DBAL\Logging\DebugStack'); + $logger = $this->getMockBuilder('Doctrine\DBAL\Logging\DebugStack')->getMock(); $logger->queries = $queries; $collector = new DoctrineDataCollector($registry); @@ -161,3 +168,11 @@ private function createCollector($queries) return $collector; } } + +class StringRepresentableClass +{ + public function __toString() + { + return 'string representation'; + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/DataFixtures/ContainerAwareLoaderTest.php b/src/Symfony/Bridge/Doctrine/Tests/DataFixtures/ContainerAwareLoaderTest.php index 53ad5a0e3a8a7..422c459b46afa 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DataFixtures/ContainerAwareLoaderTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DataFixtures/ContainerAwareLoaderTest.php @@ -11,14 +11,15 @@ namespace Symfony\Bridge\Doctrine\Tests\DataFixtures; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader; use Symfony\Bridge\Doctrine\Tests\Fixtures\ContainerAwareFixture; -class ContainerAwareLoaderTest extends \PHPUnit_Framework_TestCase +class ContainerAwareLoaderTest extends TestCase { public function testShouldSetContainerOnContainerAwareFixture() { - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); $loader = new ContainerAwareLoader($container); $fixture = new ContainerAwareFixture(); diff --git a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPassTest.php b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPassTest.php index 11bffb9c129ed..25776f86695af 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPassTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPassTest.php @@ -11,11 +11,12 @@ namespace Symfony\Bridge\Doctrine\Tests\DependencyInjection\CompilerPass; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\RegisterEventListenersAndSubscribersPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; -class RegisterEventListenersAndSubscribersPassTest extends \PHPUnit_Framework_TestCase +class RegisterEventListenersAndSubscribersPassTest extends TestCase { /** * @expectedException \InvalidArgumentException diff --git a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterMappingsPassTest.php b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterMappingsPassTest.php index c86ef117753eb..ed76f8db68dfd 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterMappingsPassTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterMappingsPassTest.php @@ -2,11 +2,12 @@ namespace Symfony\Bridge\Doctrine\Tests\DependencyInjection\CompilerPass; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\RegisterMappingsPass; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\ContainerBuilder; -class RegisterMappingsPassTest extends \PHPUnit_Framework_TestCase +class RegisterMappingsPassTest extends TestCase { /** * @expectedException \InvalidArgumentException @@ -15,7 +16,6 @@ class RegisterMappingsPassTest extends \PHPUnit_Framework_TestCase public function testNoDriverParmeterException() { $container = $this->createBuilder(array( - )); $this->process($container, array( 'manager.param.one', diff --git a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php index 9fe29a3f54bc6..135f53f493fc1 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php @@ -11,6 +11,7 @@ namespace Symfony\Bridge\Doctrine\Tests\DependencyInjection; +use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; @@ -18,7 +19,7 @@ /** * @author Fabio B. Silva */ -class DoctrineExtensionTest extends \PHPUnit_Framework_TestCase +class DoctrineExtensionTest extends TestCase { /** * @var \Symfony\Bridge\Doctrine\DependencyInjection\AbstractDoctrineExtension diff --git a/src/Symfony/Bridge/Doctrine/Tests/ExpressionLanguage/DoctrineParserCacheTest.php b/src/Symfony/Bridge/Doctrine/Tests/ExpressionLanguage/DoctrineParserCacheTest.php index 6bf22c185126a..394b1b0dfe9a2 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/ExpressionLanguage/DoctrineParserCacheTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/ExpressionLanguage/DoctrineParserCacheTest.php @@ -11,16 +11,17 @@ namespace Symfony\Bridge\Doctrine\Tests\ExpressionLanguage; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\ExpressionLanguage\DoctrineParserCache; /** * @group legacy */ -class DoctrineParserCacheTest extends \PHPUnit_Framework_TestCase +class DoctrineParserCacheTest extends TestCase { public function testFetch() { - $doctrineCacheMock = $this->getMock('Doctrine\Common\Cache\Cache'); + $doctrineCacheMock = $this->getMockBuilder('Doctrine\Common\Cache\Cache')->getMock(); $parserCache = new DoctrineParserCache($doctrineCacheMock); $doctrineCacheMock->expects($this->once()) @@ -34,7 +35,7 @@ public function testFetch() public function testFetchUnexisting() { - $doctrineCacheMock = $this->getMock('Doctrine\Common\Cache\Cache'); + $doctrineCacheMock = $this->getMockBuilder('Doctrine\Common\Cache\Cache')->getMock(); $parserCache = new DoctrineParserCache($doctrineCacheMock); $doctrineCacheMock @@ -47,7 +48,7 @@ public function testFetchUnexisting() public function testSave() { - $doctrineCacheMock = $this->getMock('Doctrine\Common\Cache\Cache'); + $doctrineCacheMock = $this->getMockBuilder('Doctrine\Common\Cache\Cache')->getMock(); $parserCache = new DoctrineParserCache($doctrineCacheMock); $expression = $this->getMockBuilder('Symfony\Component\ExpressionLanguage\ParsedExpression') diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeObjectNoToStringIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeObjectNoToStringIdEntity.php new file mode 100644 index 0000000000000..ac97367094bd5 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeObjectNoToStringIdEntity.php @@ -0,0 +1,53 @@ +objectOne = $objectOne; + $this->objectTwo = $objectTwo; + } + + /** + * @return SingleIntIdNoToStringEntity + */ + public function getObjectOne() + { + return $this->objectOne; + } + + /** + * @return SingleIntIdNoToStringEntity + */ + public function getObjectTwo() + { + return $this->objectTwo; + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoubleNullableNameEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoubleNullableNameEntity.php new file mode 100644 index 0000000000000..29b247b9b1c36 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoubleNullableNameEntity.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Fixtures; + +use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Entity; + +/** @Entity */ +class DoubleNullableNameEntity +{ + /** @Id @Column(type="integer") */ + protected $id; + + /** @Column(type="string", nullable=true) */ + public $name; + + /** @Column(type="string", nullable=true) */ + public $name2; + + public function __construct($id, $name, $name2) + { + $this->id = $id; + $this->name = $name; + $this->name2 = $name2; + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdStringWrapperNameEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdStringWrapperNameEntity.php new file mode 100644 index 0000000000000..d69d3de4b6f34 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdStringWrapperNameEntity.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Fixtures; + +use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Entity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\Type\StringWrapper; + +/** @Entity */ +class SingleIntIdStringWrapperNameEntity +{ + /** @Id @Column(type="integer") */ + protected $id; + + /** @Column(type="string_wrapper", nullable=true) */ + public $name; + + public function __construct($id, $name) + { + $this->id = $id; + $this->name = $name; + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Type/StringWrapper.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Type/StringWrapper.php new file mode 100644 index 0000000000000..13bb3703d78d8 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Type/StringWrapper.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Fixtures\Type; + +class StringWrapper +{ + /** + * @var string + */ + private $string; + + /** + * @param string $string + */ + public function __construct($string = null) + { + $this->string = $string; + } + + /** + * @return string + */ + public function getString() + { + return $this->string; + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Type/StringWrapperType.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Type/StringWrapperType.php new file mode 100644 index 0000000000000..0af4271ba73fa --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Type/StringWrapperType.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Fixtures\Type; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Types\StringType; + +class StringWrapperType extends StringType +{ + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return $value instanceof StringWrapper ? $value->getString() : null; + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return new StringWrapper($value); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'string_wrapper'; + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/DoctrineChoiceLoaderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/DoctrineChoiceLoaderTest.php index 6444fd1bf0de3..dfe2b00cdfd56 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/DoctrineChoiceLoaderTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/DoctrineChoiceLoaderTest.php @@ -14,6 +14,7 @@ use Doctrine\Common\Persistence\ObjectManager; use Doctrine\Common\Persistence\ObjectRepository; use Doctrine\ORM\Mapping\ClassMetadata; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\Form\ChoiceList\DoctrineChoiceLoader; use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityLoaderInterface; use Symfony\Bridge\Doctrine\Form\ChoiceList\IdReader; @@ -22,7 +23,7 @@ /** * @author Bernhard Schussek */ -class DoctrineChoiceLoaderTest extends \PHPUnit_Framework_TestCase +class DoctrineChoiceLoaderTest extends TestCase { /** * @var ObjectManager|\PHPUnit_Framework_MockObject_MockObject @@ -66,14 +67,14 @@ class DoctrineChoiceLoaderTest extends \PHPUnit_Framework_TestCase protected function setUp() { - $this->factory = $this->getMock('Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface'); - $this->om = $this->getMock('Doctrine\Common\Persistence\ObjectManager'); - $this->repository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); + $this->factory = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface')->getMock(); + $this->om = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')->getMock(); + $this->repository = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectRepository')->getMock(); $this->class = 'stdClass'; $this->idReader = $this->getMockBuilder('Symfony\Bridge\Doctrine\Form\ChoiceList\IdReader') ->disableOriginalConstructor() ->getMock(); - $this->objectLoader = $this->getMock('Symfony\Bridge\Doctrine\Form\ChoiceList\EntityLoaderInterface'); + $this->objectLoader = $this->getMockBuilder('Symfony\Bridge\Doctrine\Form\ChoiceList\EntityLoaderInterface')->getMock(); $this->obj1 = (object) array('name' => 'A'); $this->obj2 = (object) array('name' => 'B'); $this->obj3 = (object) array('name' => 'C'); @@ -117,7 +118,7 @@ public function testLoadChoiceList() */ public function testLegacyLoadChoiceList() { - $factory = $this->getMock('Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface'); + $factory = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface')->getMock(); $loader = new DoctrineChoiceLoader( $factory, $this->om, diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php index a14b786b8d737..29486af84a541 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php @@ -11,12 +11,13 @@ namespace Symfony\Bridge\Doctrine\Tests\Form\ChoiceList; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\Form\ChoiceList\ORMQueryBuilderLoader; use Symfony\Bridge\Doctrine\Test\DoctrineTestHelper; use Doctrine\DBAL\Connection; use Doctrine\ORM\Version; -class ORMQueryBuilderLoaderTest extends \PHPUnit_Framework_TestCase +class ORMQueryBuilderLoaderTest extends TestCase { public function testIdentifierTypeIsStringArray() { diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/DataTransformer/CollectionToArrayTransformerTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/DataTransformer/CollectionToArrayTransformerTest.php index 50b9bcd19e172..fa3ff911ad355 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/DataTransformer/CollectionToArrayTransformerTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/DataTransformer/CollectionToArrayTransformerTest.php @@ -12,12 +12,13 @@ namespace Symfony\Bridge\Doctrine\Tests\Form\DataTransformer; use Doctrine\Common\Collections\ArrayCollection; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\Form\DataTransformer\CollectionToArrayTransformer; /** * @author Bernhard Schussek */ -class CollectionToArrayTransformerTest extends \PHPUnit_Framework_TestCase +class CollectionToArrayTransformerTest extends TestCase { /** * @var CollectionToArrayTransformer diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/DoctrineOrmTypeGuesserTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/DoctrineOrmTypeGuesserTest.php index 0cb900f6d04c4..12296729e2d87 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/DoctrineOrmTypeGuesserTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/DoctrineOrmTypeGuesserTest.php @@ -12,11 +12,12 @@ namespace Symfony\Bridge\Doctrine\Tests\Form; use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\Form\DoctrineOrmTypeGuesser; use Symfony\Component\Form\Guess\Guess; use Symfony\Component\Form\Guess\ValueGuess; -class DoctrineOrmTypeGuesserTest extends \PHPUnit_Framework_TestCase +class DoctrineOrmTypeGuesserTest extends TestCase { /** * @dataProvider requiredProvider @@ -86,10 +87,10 @@ public function requiredProvider() private function getGuesser(ClassMetadata $classMetadata) { - $em = $this->getMock('Doctrine\Common\Persistence\ObjectManager'); + $em = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')->getMock(); $em->expects($this->once())->method('getClassMetaData')->with('TestEntity')->will($this->returnValue($classMetadata)); - $registry = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry'); + $registry = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')->getMock(); $registry->expects($this->once())->method('getManagers')->will($this->returnValue(array($em))); return new DoctrineOrmTypeGuesser($registry); diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/EventListener/MergeDoctrineCollectionListenerTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/EventListener/MergeDoctrineCollectionListenerTest.php index 8c4ec7a2153f4..dbcbc0f325e4b 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/EventListener/MergeDoctrineCollectionListenerTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/EventListener/MergeDoctrineCollectionListenerTest.php @@ -12,13 +12,14 @@ namespace Symfony\Bridge\Doctrine\Tests\Form\EventListener; use Doctrine\Common\Collections\ArrayCollection; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\Form\EventListener\MergeDoctrineCollectionListener; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; -class MergeDoctrineCollectionListenerTest extends \PHPUnit_Framework_TestCase +class MergeDoctrineCollectionListenerTest extends TestCase { /** @var \Doctrine\Common\Collections\ArrayCollection */ private $collection; @@ -31,7 +32,7 @@ protected function setUp() { $this->collection = new ArrayCollection(array('test')); $this->dispatcher = new EventDispatcher(); - $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + $this->factory = $this->getMockBuilder('Symfony\Component\Form\FormFactoryInterface')->getMock(); $this->form = $this->getBuilder() ->getForm(); } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php index 57df4195bcec4..ac96ea21c5312 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php @@ -32,7 +32,7 @@ class EntityTypePerformanceTest extends FormPerformanceTestCase protected function getExtensions() { - $manager = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry'); + $manager = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')->getMock(); $manager->expects($this->any()) ->method('getManager') @@ -72,7 +72,7 @@ protected function setUp() $ids = range(1, 300); foreach ($ids as $id) { - $name = 65 + chr($id % 57); + $name = 65 + (int) chr($id % 57); $this->em->persist(new SingleIntIdEntity($id, $name)); } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php index f5752fb70750e..065050199951a 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php @@ -29,13 +29,16 @@ use Symfony\Component\Form\ChoiceList\View\ChoiceGroupView; use Symfony\Component\Form\ChoiceList\View\ChoiceView; use Symfony\Component\Form\Forms; -use Symfony\Component\Form\Test\TypeTestCase; +use Symfony\Component\Form\Tests\Extension\Core\Type\BaseTypeTest; +use Symfony\Component\Form\Tests\Extension\Core\Type\FormTypeTest; use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleAssociationToIntIdEntity; use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdNoToStringEntity; -class EntityTypeTest extends TypeTestCase +class EntityTypeTest extends BaseTypeTest { + const TESTED_TYPE = 'Symfony\Bridge\Doctrine\Form\Type\EntityType'; + const ITEM_GROUP_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\GroupableEntity'; const SINGLE_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity'; const SINGLE_IDENT_NO_TO_STRING_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdNoToStringEntity'; @@ -116,7 +119,7 @@ protected function persist(array $entities) */ public function testClassOptionIsRequired() { - $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType'); + $this->factory->createNamed('name', static::TESTED_TYPE); } /** @@ -124,7 +127,7 @@ public function testClassOptionIsRequired() */ public function testInvalidClassOption() { - $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'class' => 'foo', )); } @@ -136,7 +139,7 @@ public function testSetDataToUninitializedEntityWithNonRequired() $this->persist(array($entity1, $entity2)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'required' => false, @@ -153,13 +156,14 @@ public function testSetDataToUninitializedEntityWithNonRequiredToString() $this->persist(array($entity1, $entity2)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $view = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'required' => false, - )); + )) + ->createView(); - $this->assertEquals(array(1 => new ChoiceView($entity1, '1', 'Foo'), 2 => new ChoiceView($entity2, '2', 'Bar')), $field->createView()->vars['choices']); + $this->assertEquals(array(1 => new ChoiceView($entity1, '1', 'Foo'), 2 => new ChoiceView($entity2, '2', 'Bar')), $view->vars['choices']); } public function testSetDataToUninitializedEntityWithNonRequiredQueryBuilder() @@ -170,15 +174,16 @@ public function testSetDataToUninitializedEntityWithNonRequiredQueryBuilder() $this->persist(array($entity1, $entity2)); $qb = $this->em->createQueryBuilder()->select('e')->from(self::SINGLE_IDENT_CLASS, 'e'); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $view = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'required' => false, 'choice_label' => 'name', 'query_builder' => $qb, - )); + )) + ->createView(); - $this->assertEquals(array(1 => new ChoiceView($entity1, '1', 'Foo'), 2 => new ChoiceView($entity2, '2', 'Bar')), $field->createView()->vars['choices']); + $this->assertEquals(array(1 => new ChoiceView($entity1, '1', 'Foo'), 2 => new ChoiceView($entity2, '2', 'Bar')), $view->vars['choices']); } /** @@ -186,7 +191,7 @@ public function testSetDataToUninitializedEntityWithNonRequiredQueryBuilder() */ public function testConfigureQueryBuilderWithNonQueryBuilderAndNonClosure() { - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => new \stdClass(), @@ -198,7 +203,7 @@ public function testConfigureQueryBuilderWithNonQueryBuilderAndNonClosure() */ public function testConfigureQueryBuilderWithClosureReturningNonQueryBuilder() { - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => function () { @@ -216,7 +221,7 @@ public function testConfigureQueryBuilderWithClosureReturningNullUseDefault() $this->persist(array($entity1, $entity2)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => function () { @@ -229,7 +234,7 @@ public function testConfigureQueryBuilderWithClosureReturningNullUseDefault() public function testSetDataSingleNull() { - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'multiple' => false, 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, @@ -242,7 +247,7 @@ public function testSetDataSingleNull() public function testSetDataMultipleExpandedNull() { - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'multiple' => true, 'expanded' => true, 'em' => 'default', @@ -256,7 +261,7 @@ public function testSetDataMultipleExpandedNull() public function testSetDataMultipleNonExpandedNull() { - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'multiple' => true, 'expanded' => false, 'em' => 'default', @@ -268,47 +273,6 @@ public function testSetDataMultipleNonExpandedNull() $this->assertSame(array(), $field->getViewData()); } - public function testSubmitSingleExpandedNull() - { - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( - 'multiple' => false, - 'expanded' => true, - 'em' => 'default', - 'class' => self::SINGLE_IDENT_CLASS, - )); - $field->submit(null); - - $this->assertNull($field->getData()); - $this->assertSame('', $field->getViewData(), 'View data is always a string'); - } - - public function testSubmitSingleNonExpandedNull() - { - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( - 'multiple' => false, - 'expanded' => false, - 'em' => 'default', - 'class' => self::SINGLE_IDENT_CLASS, - )); - $field->submit(null); - - $this->assertNull($field->getData()); - $this->assertSame('', $field->getViewData()); - } - - public function testSubmitMultipleNull() - { - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( - 'multiple' => true, - 'em' => 'default', - 'class' => self::SINGLE_IDENT_CLASS, - )); - $field->submit(null); - - $this->assertEquals(new ArrayCollection(), $field->getData()); - $this->assertSame(array(), $field->getViewData()); - } - public function testSubmitSingleNonExpandedSingleIdentifier() { $entity1 = new SingleIntIdEntity(1, 'Foo'); @@ -316,7 +280,7 @@ public function testSubmitSingleNonExpandedSingleIdentifier() $this->persist(array($entity1, $entity2)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'multiple' => false, 'expanded' => false, 'em' => 'default', @@ -341,7 +305,7 @@ public function testSubmitSingleNonExpandedSingleAssocIdentifier() $this->persist(array($innerEntity1, $innerEntity2, $entity1, $entity2)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'multiple' => false, 'expanded' => false, 'em' => 'default', @@ -363,7 +327,7 @@ public function testSubmitSingleNonExpandedCompositeIdentifier() $this->persist(array($entity1, $entity2)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'multiple' => false, 'expanded' => false, 'em' => 'default', @@ -387,7 +351,7 @@ public function testSubmitMultipleNonExpandedSingleIdentifier() $this->persist(array($entity1, $entity2, $entity3)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'multiple' => true, 'expanded' => false, 'em' => 'default', @@ -416,7 +380,7 @@ public function testSubmitMultipleNonExpandedSingleAssocIdentifier() $this->persist(array($innerEntity1, $innerEntity2, $innerEntity3, $entity1, $entity2, $entity3)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'multiple' => true, 'expanded' => false, 'em' => 'default', @@ -441,7 +405,7 @@ public function testSubmitMultipleNonExpandedSingleIdentifierForExistingData() $this->persist(array($entity1, $entity2, $entity3)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'multiple' => true, 'expanded' => false, 'em' => 'default', @@ -472,7 +436,7 @@ public function testSubmitMultipleNonExpandedCompositeIdentifier() $this->persist(array($entity1, $entity2, $entity3)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'multiple' => true, 'expanded' => false, 'em' => 'default', @@ -498,7 +462,7 @@ public function testSubmitMultipleNonExpandedCompositeIdentifierExistingData() $this->persist(array($entity1, $entity2, $entity3)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'multiple' => true, 'expanded' => false, 'em' => 'default', @@ -528,7 +492,7 @@ public function testSubmitSingleExpanded() $this->persist(array($entity1, $entity2)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'multiple' => false, 'expanded' => true, 'em' => 'default', @@ -554,7 +518,7 @@ public function testSubmitMultipleExpanded() $this->persist(array($entity1, $entity2, $entity3)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'multiple' => true, 'expanded' => true, 'em' => 'default', @@ -583,7 +547,7 @@ public function testSubmitMultipleExpandedWithNegativeIntegerId() $this->persist(array($entity1, $entity2)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'multiple' => true, 'expanded' => true, 'em' => 'default', @@ -608,7 +572,7 @@ public function testSubmitSingleNonExpandedStringCastableIdentifier() $this->persist(array($entity1, $entity2)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'multiple' => false, 'expanded' => false, 'em' => 'default', @@ -630,7 +594,7 @@ public function testSubmitSingleStringCastableIdentifierExpanded() $this->persist(array($entity1, $entity2)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'multiple' => false, 'expanded' => true, 'em' => 'default', @@ -656,7 +620,7 @@ public function testSubmitMultipleNonExpandedStringCastableIdentifierForExisting $this->persist(array($entity1, $entity2, $entity3)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'multiple' => true, 'expanded' => false, 'em' => 'default', @@ -687,7 +651,7 @@ public function testSubmitMultipleNonExpandedStringCastableIdentifier() $this->persist(array($entity1, $entity2, $entity3)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'multiple' => true, 'expanded' => false, 'em' => 'default', @@ -712,7 +676,7 @@ public function testSubmitMultipleStringCastableIdentifierExpanded() $this->persist(array($entity1, $entity2, $entity3)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'multiple' => true, 'expanded' => true, 'em' => 'default', @@ -742,7 +706,7 @@ public function testOverrideChoices() $this->persist(array($entity1, $entity2, $entity3)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, // not all persisted entities should be displayed @@ -765,7 +729,7 @@ public function testOverrideChoicesValues() $this->persist(array($entity1, $entity2)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'choice_label' => 'name', @@ -787,7 +751,7 @@ public function testOverrideChoicesValuesWithCallable() $this->persist(array($entity1, $entity2)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'em' => 'default', 'class' => self::ITEM_GROUP_CLASS, 'choice_label' => 'name', @@ -818,7 +782,7 @@ public function testChoicesForValuesOptimization() $this->persist(array($entity1, $entity2)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'choice_label' => 'name', @@ -844,7 +808,7 @@ public function testGroupByChoices() $this->persist(array($item1, $item2, $item3, $item4)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'em' => 'default', 'class' => self::ITEM_GROUP_CLASS, 'choices' => array($item1, $item2, $item3, $item4), @@ -875,7 +839,7 @@ public function testPreferredChoices() $this->persist(array($entity1, $entity2, $entity3)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'preferred_choices' => array($entity3, $entity2), @@ -894,7 +858,7 @@ public function testOverrideChoicesWithPreferredChoices() $this->persist(array($entity1, $entity2, $entity3)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'choices' => array($entity2, $entity3), @@ -914,7 +878,7 @@ public function testDisallowChoicesThatAreNotIncludedChoicesSingleIdentifier() $this->persist(array($entity1, $entity2, $entity3)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'choices' => array($entity1, $entity2), @@ -937,7 +901,7 @@ public function testDisallowChoicesThatAreNotIncludedChoicesSingleAssocIdentifie $this->persist(array($innerEntity1, $innerEntity2, $entity1, $entity2)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'em' => 'default', 'class' => self::SINGLE_ASSOC_IDENT_CLASS, 'choices' => array($entity1, $entity2), @@ -958,7 +922,7 @@ public function testDisallowChoicesThatAreNotIncludedChoicesCompositeIdentifier( $this->persist(array($entity1, $entity2, $entity3)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'em' => 'default', 'class' => self::COMPOSITE_IDENT_CLASS, 'choices' => array($entity1, $entity2), @@ -981,7 +945,7 @@ public function testDisallowChoicesThatAreNotIncludedQueryBuilderSingleIdentifie $repository = $this->em->getRepository(self::SINGLE_IDENT_CLASS); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => $repository->createQueryBuilder('e') @@ -1009,7 +973,7 @@ public function testDisallowChoicesThatAreNotIncludedQueryBuilderSingleAssocIden $repository = $this->em->getRepository(self::SINGLE_ASSOC_IDENT_CLASS); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'em' => 'default', 'class' => self::SINGLE_ASSOC_IDENT_CLASS, 'query_builder' => $repository->createQueryBuilder('e') @@ -1031,10 +995,10 @@ public function testDisallowChoicesThatAreNotIncludedQueryBuilderAsClosureSingle $this->persist(array($entity1, $entity2, $entity3)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, - 'query_builder' => function ($repository) { + 'query_builder' => function (EntityRepository $repository) { return $repository->createQueryBuilder('e') ->where('e.id IN (1, 2)'); }, @@ -1055,10 +1019,10 @@ public function testDisallowChoicesThatAreNotIncludedQueryBuilderAsClosureCompos $this->persist(array($entity1, $entity2, $entity3)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'em' => 'default', 'class' => self::COMPOSITE_IDENT_CLASS, - 'query_builder' => function ($repository) { + 'query_builder' => function (EntityRepository $repository) { return $repository->createQueryBuilder('e') ->where('e.id1 IN (10, 50)'); }, @@ -1077,7 +1041,7 @@ public function testSubmitSingleStringIdentifier() $this->persist(array($entity1)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'multiple' => false, 'expanded' => false, 'em' => 'default', @@ -1098,7 +1062,7 @@ public function testSubmitCompositeStringIdentifier() $this->persist(array($entity1)); - $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'multiple' => false, 'expanded' => false, 'em' => 'default', @@ -1124,7 +1088,7 @@ public function testGetManagerForClassIfNoEm() ->with(self::SINGLE_IDENT_CLASS) ->will($this->returnValue($this->em)); - $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'class' => self::SINGLE_IDENT_CLASS, 'required' => false, 'choice_label' => 'name', @@ -1139,7 +1103,7 @@ public function testExplicitEm() $this->emRegistry->expects($this->never()) ->method('getManagerForClass'); - $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $this->factory->createNamed('name', static::TESTED_TYPE, null, array( 'em' => $this->em, 'class' => self::SINGLE_IDENT_CLASS, 'choice_label' => 'name', @@ -1168,15 +1132,15 @@ public function testLoaderCaching() ->addTypeGuesser($entityTypeGuesser) ->getFormFactory(); - $formBuilder = $factory->createNamedBuilder('form', 'Symfony\Component\Form\Extension\Core\Type\FormType'); + $formBuilder = $factory->createNamedBuilder('form', FormTypeTest::TESTED_TYPE); - $formBuilder->add('property1', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', array( + $formBuilder->add('property1', static::TESTED_TYPE, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => $repo->createQueryBuilder('e')->where('e.id IN (1, 2)'), )); - $formBuilder->add('property2', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', array( + $formBuilder->add('property2', static::TESTED_TYPE, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => function (EntityRepository $repo) { @@ -1184,7 +1148,7 @@ public function testLoaderCaching() }, )); - $formBuilder->add('property3', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', array( + $formBuilder->add('property3', static::TESTED_TYPE, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => function (EntityRepository $repo) { @@ -1231,15 +1195,15 @@ public function testLoaderCachingWithParameters() ->addTypeGuesser($entityTypeGuesser) ->getFormFactory(); - $formBuilder = $factory->createNamedBuilder('form', 'Symfony\Component\Form\Extension\Core\Type\FormType'); + $formBuilder = $factory->createNamedBuilder('form', FormTypeTest::TESTED_TYPE); - $formBuilder->add('property1', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', array( + $formBuilder->add('property1', static::TESTED_TYPE, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => $repo->createQueryBuilder('e')->where('e.id = :id')->setParameter('id', 1), )); - $formBuilder->add('property2', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', array( + $formBuilder->add('property2', static::TESTED_TYPE, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => function (EntityRepository $repo) { @@ -1247,7 +1211,7 @@ public function testLoaderCachingWithParameters() }, )); - $formBuilder->add('property3', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', array( + $formBuilder->add('property3', static::TESTED_TYPE, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => function (EntityRepository $repo) { @@ -1274,7 +1238,7 @@ public function testLoaderCachingWithParameters() protected function createRegistryMock($name, $em) { - $registry = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry'); + $registry = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')->getMock(); $registry->expects($this->any()) ->method('getManager') ->with($this->equalTo($name)) @@ -1282,4 +1246,213 @@ protected function createRegistryMock($name, $em) return $registry; } + + public function testPassDisabledAsOption() + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'em' => 'default', + 'disabled' => true, + 'class' => self::SINGLE_IDENT_CLASS, + )); + + $this->assertTrue($form->isDisabled()); + } + + public function testPassIdAndNameToView() + { + $view = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + )) + ->createView(); + + $this->assertEquals('name', $view->vars['id']); + $this->assertEquals('name', $view->vars['name']); + $this->assertEquals('name', $view->vars['full_name']); + } + + public function testStripLeadingUnderscoresAndDigitsFromId() + { + $view = $this->factory->createNamed('_09name', static::TESTED_TYPE, null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + )) + ->createView(); + + $this->assertEquals('name', $view->vars['id']); + $this->assertEquals('_09name', $view->vars['name']); + $this->assertEquals('_09name', $view->vars['full_name']); + } + + public function testPassIdAndNameToViewWithParent() + { + $view = $this->factory->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE) + ->add('child', static::TESTED_TYPE, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + )) + ->getForm() + ->createView(); + + $this->assertEquals('parent_child', $view['child']->vars['id']); + $this->assertEquals('child', $view['child']->vars['name']); + $this->assertEquals('parent[child]', $view['child']->vars['full_name']); + } + + public function testPassIdAndNameToViewWithGrandParent() + { + $builder = $this->factory->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE) + ->add('child', FormTypeTest::TESTED_TYPE); + $builder->get('child')->add('grand_child', static::TESTED_TYPE, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + )); + $view = $builder->getForm()->createView(); + + $this->assertEquals('parent_child_grand_child', $view['child']['grand_child']->vars['id']); + $this->assertEquals('grand_child', $view['child']['grand_child']->vars['name']); + $this->assertEquals('parent[child][grand_child]', $view['child']['grand_child']->vars['full_name']); + } + + public function testPassTranslationDomainToView() + { + $view = $this->factory->create(static::TESTED_TYPE, null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'translation_domain' => 'domain', + )) + ->createView(); + + $this->assertSame('domain', $view->vars['translation_domain']); + } + + public function testInheritTranslationDomainFromParent() + { + $view = $this->factory + ->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE, null, array( + 'translation_domain' => 'domain', + )) + ->add('child', static::TESTED_TYPE, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + )) + ->getForm() + ->createView(); + + $this->assertEquals('domain', $view['child']->vars['translation_domain']); + } + + public function testPreferOwnTranslationDomain() + { + $view = $this->factory + ->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE, null, array( + 'translation_domain' => 'parent_domain', + )) + ->add('child', static::TESTED_TYPE, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'translation_domain' => 'domain', + )) + ->getForm() + ->createView(); + + $this->assertEquals('domain', $view['child']->vars['translation_domain']); + } + + public function testDefaultTranslationDomain() + { + $view = $this->factory + ->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE) + ->add('child', static::TESTED_TYPE, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + )) + ->getForm() + ->createView(); + + $this->assertNull($view['child']->vars['translation_domain']); + } + + public function testPassLabelToView() + { + $view = $this->factory->createNamed('__test___field', static::TESTED_TYPE, null, array( + 'label' => 'My label', + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + )) + ->createView(); + + $this->assertSame('My label', $view->vars['label']); + } + + public function testPassMultipartFalseToView() + { + $view = $this->factory->create(static::TESTED_TYPE, null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + )) + ->createView(); + + $this->assertFalse($view->vars['multipart']); + } + + public function testSubmitNull($expected = null, $norm = null, $view = null) + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + )); + $form->submit(null); + + $this->assertNull($form->getData()); + $this->assertNull($form->getNormData()); + $this->assertSame('', $form->getViewData(), 'View data is always a string'); + } + + public function testSubmitNullExpanded() + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'expanded' => true, + )); + $form->submit(null); + + $this->assertNull($form->getData()); + $this->assertNull($form->getNormData()); + $this->assertSame('', $form->getViewData(), 'View data is always a string'); + } + + public function testSubmitNullMultiple() + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'multiple' => true, + )); + $form->submit(null); + + $collection = new ArrayCollection(); + + $this->assertEquals($collection, $form->getData()); + $this->assertEquals($collection, $form->getNormData()); + $this->assertSame(array(), $form->getViewData(), 'View data is always an array'); + } + + public function testSubmitNullExpandedMultiple() + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'expanded' => true, + 'multiple' => true, + )); + $form->submit(null); + + $collection = new ArrayCollection(); + + $this->assertEquals($collection, $form->getData()); + $this->assertEquals($collection, $form->getNormData()); + $this->assertSame(array(), $form->getViewData(), 'View data is always an array'); + } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/HttpFoundation/DbalSessionHandlerTest.php b/src/Symfony/Bridge/Doctrine/Tests/HttpFoundation/DbalSessionHandlerTest.php index 4f82bc37bbb79..1c9c82bc129e6 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/HttpFoundation/DbalSessionHandlerTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/HttpFoundation/DbalSessionHandlerTest.php @@ -11,6 +11,7 @@ namespace Symfony\Bridge\Doctrine\Tests\HttpFoundation; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\HttpFoundation\DbalSessionHandler; /** @@ -18,11 +19,13 @@ * * @author Drak */ -class DbalSessionHandlerTest extends \PHPUnit_Framework_TestCase +class DbalSessionHandlerTest extends TestCase { public function testConstruct() { $connection = $this->getMockBuilder('Doctrine\DBAL\Connection')->disableOriginalConstructor()->getMock(); $handler = new DbalSessionHandler($connection); + + $this->assertInstanceOf('Symfony\Bridge\Doctrine\HttpFoundation\DbalSessionHandler', $handler); } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Logger/DbalLoggerTest.php b/src/Symfony/Bridge/Doctrine/Tests/Logger/DbalLoggerTest.php index 6acc47fe904e0..1e3e6ca6ec809 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Logger/DbalLoggerTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Logger/DbalLoggerTest.php @@ -11,16 +11,17 @@ namespace Symfony\Bridge\Doctrine\Tests\Logger; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\Logger\DbalLogger; -class DbalLoggerTest extends \PHPUnit_Framework_TestCase +class DbalLoggerTest extends TestCase { /** * @dataProvider getLogFixtures */ public function testLog($sql, $params, $logParams) { - $logger = $this->getMock('Psr\\Log\\LoggerInterface'); + $logger = $this->getMockBuilder('Psr\\Log\\LoggerInterface')->getMock(); $dbalLogger = $this ->getMockBuilder('Symfony\\Bridge\\Doctrine\\Logger\\DbalLogger') @@ -52,7 +53,7 @@ public function getLogFixtures() public function testLogNonUtf8() { - $logger = $this->getMock('Psr\\Log\\LoggerInterface'); + $logger = $this->getMockBuilder('Psr\\Log\\LoggerInterface')->getMock(); $dbalLogger = $this ->getMockBuilder('Symfony\\Bridge\\Doctrine\\Logger\\DbalLogger') @@ -75,7 +76,7 @@ public function testLogNonUtf8() public function testLogNonUtf8Array() { - $logger = $this->getMock('Psr\\Log\\LoggerInterface'); + $logger = $this->getMockBuilder('Psr\\Log\\LoggerInterface')->getMock(); $dbalLogger = $this ->getMockBuilder('Symfony\\Bridge\\Doctrine\\Logger\\DbalLogger') @@ -106,7 +107,7 @@ public function testLogNonUtf8Array() public function testLogLongString() { - $logger = $this->getMock('Psr\\Log\\LoggerInterface'); + $logger = $this->getMockBuilder('Psr\\Log\\LoggerInterface')->getMock(); $dbalLogger = $this ->getMockBuilder('Symfony\\Bridge\\Doctrine\\Logger\\DbalLogger') @@ -134,7 +135,7 @@ public function testLogLongString() public function testLogUTF8LongString() { - $logger = $this->getMock('Psr\\Log\\LoggerInterface'); + $logger = $this->getMockBuilder('Psr\\Log\\LoggerInterface')->getMock(); $dbalLogger = $this ->getMockBuilder('Symfony\\Bridge\\Doctrine\\Logger\\DbalLogger') diff --git a/src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php b/src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php index 30e3aef900be1..fa3037a609d8a 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php @@ -11,13 +11,17 @@ namespace Symfony\Bridge\Doctrine\Tests; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\ManagerRegistry; use Symfony\Bridge\ProxyManager\Tests\LazyProxy\Dumper\PhpDumperTest; -class ManagerRegistryTest extends \PHPUnit_Framework_TestCase +class ManagerRegistryTest extends TestCase { public static function setUpBeforeClass() { + if (!class_exists('PHPUnit_Framework_TestCase')) { + self::markTestSkipped('proxy-manager-bridge is not yet compatible with namespaced phpunit versions.'); + } $test = new PhpDumperTest(); $test->testDumpContainerWithProxyServiceWillShareProxies(); } diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php index 2224517db6708..277922daefd51 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php @@ -14,13 +14,14 @@ use Doctrine\DBAL\Types\Type as DBALType; use Doctrine\ORM\EntityManager; use Doctrine\ORM\Tools\Setup; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor; use Symfony\Component\PropertyInfo\Type; /** * @author Kévin Dunglas */ -class DoctrineExtractorTest extends \PHPUnit_Framework_TestCase +class DoctrineExtractorTest extends TestCase { /** * @var DoctrineExtractor diff --git a/src/Symfony/Bridge/Doctrine/Tests/Security/User/EntityUserProviderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Security/User/EntityUserProviderTest.php index 3a3a8456e20ab..78a2cdb6f33b3 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Security/User/EntityUserProviderTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Security/User/EntityUserProviderTest.php @@ -11,12 +11,13 @@ namespace Symfony\Bridge\Doctrine\Tests\Security\User; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\Test\DoctrineTestHelper; use Symfony\Bridge\Doctrine\Tests\Fixtures\User; use Symfony\Bridge\Doctrine\Security\User\EntityUserProvider; use Doctrine\ORM\Tools\SchemaTool; -class EntityUserProviderTest extends \PHPUnit_Framework_TestCase +class EntityUserProviderTest extends TestCase { public function testRefreshUserGetsUserByPrimaryKey() { @@ -104,7 +105,7 @@ public function testRefreshUserRequiresId() $user1 = new User(null, null, 'user1'); $provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\User', 'name'); - $this->setExpectedException( + $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}( 'InvalidArgumentException', 'You cannot refresh a user from the EntityUserProvider that does not contain an identifier. The user object has to be serialized with its own identifier mapped by Doctrine' ); @@ -124,7 +125,7 @@ public function testRefreshInvalidUser() $provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\User', 'name'); $user2 = new User(1, 2, 'user2'); - $this->setExpectedException( + $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}( 'Symfony\Component\Security\Core\Exception\UsernameNotFoundException', 'User with id {"id1":1,"id2":2} not found' ); @@ -150,12 +151,12 @@ public function testSupportProxy() public function testLoadUserByUserNameShouldLoadUserWhenProperInterfaceProvided() { - $repository = $this->getMock('\Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface'); + $repository = $this->getMockBuilder('\Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface')->getMock(); $repository->expects($this->once()) ->method('loadUserByUsername') ->with('name') ->willReturn( - $this->getMock('\Symfony\Component\Security\Core\User\UserInterface') + $this->getMockBuilder('\Symfony\Component\Security\Core\User\UserInterface')->getMock() ); $provider = new EntityUserProvider( @@ -171,7 +172,7 @@ public function testLoadUserByUserNameShouldLoadUserWhenProperInterfaceProvided( */ public function testLoadUserByUserNameShouldDeclineInvalidInterface() { - $repository = $this->getMock('\Symfony\Component\Security\Core\User\AdvancedUserInterface'); + $repository = $this->getMockBuilder('\Symfony\Component\Security\Core\User\AdvancedUserInterface')->getMock(); $provider = new EntityUserProvider( $this->getManager($this->getObjectManager($repository)), @@ -183,7 +184,7 @@ public function testLoadUserByUserNameShouldDeclineInvalidInterface() private function getManager($em, $name = null) { - $manager = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry'); + $manager = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')->getMock(); $manager->expects($this->any()) ->method('getManager') ->with($this->equalTo($name)) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php index 21d3950313590..0045e76cbca83 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php @@ -15,15 +15,20 @@ use Doctrine\Common\Persistence\ManagerRegistry; use Doctrine\Common\Persistence\ObjectManager; use Doctrine\Common\Persistence\ObjectRepository; +use Doctrine\DBAL\Types\Type; use Symfony\Bridge\Doctrine\Test\DoctrineTestHelper; use Symfony\Bridge\Doctrine\Test\TestRepositoryFactory; use Symfony\Bridge\Doctrine\Tests\Fixtures\Employee; use Symfony\Bridge\Doctrine\Tests\Fixtures\Person; +use Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeObjectNoToStringIdEntity; use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity; use Symfony\Bridge\Doctrine\Tests\Fixtures\DoubleNameEntity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\DoubleNullableNameEntity; use Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity; use Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity2; use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdNoToStringEntity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdStringWrapperNameEntity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\Type\StringWrapper; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntityValidator; use Symfony\Component\Validator\Tests\Constraints\AbstractConstraintValidatorTest; @@ -62,6 +67,10 @@ protected function setUp() $config = DoctrineTestHelper::createTestConfiguration(); $config->setRepositoryFactory($this->repositoryFactory); + if (!Type::hasType('string_wrapper')) { + Type::addType('string_wrapper', 'Symfony\Bridge\Doctrine\Tests\Fixtures\Type\StringWrapperType'); + } + $this->em = DoctrineTestHelper::createTestEntityManager($config); $this->registry = $this->createRegistryMock($this->em); $this->createSchema($this->em); @@ -71,7 +80,7 @@ protected function setUp() protected function createRegistryMock(ObjectManager $em = null) { - $registry = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry'); + $registry = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')->getMock(); $registry->expects($this->any()) ->method('getManager') ->with($this->equalTo(self::EM_NAME)) @@ -100,7 +109,7 @@ protected function createEntityManagerMock($repositoryMock) ->will($this->returnValue($repositoryMock)) ; - $classMetadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata'); + $classMetadata = $this->getMockBuilder('Doctrine\Common\Persistence\Mapping\ClassMetadata')->getMock(); $classMetadata ->expects($this->any()) ->method('hasField') @@ -141,11 +150,14 @@ private function createSchema(ObjectManager $em) $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity'), $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdNoToStringEntity'), $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\DoubleNameEntity'), + $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\DoubleNullableNameEntity'), $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIntIdEntity'), $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity'), $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity2'), $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\Person'), $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\Employee'), + $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeObjectNoToStringIdEntity'), + $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdStringWrapperNameEntity'), )); } @@ -179,7 +191,7 @@ public function testValidateUniqueness() $this->buildViolation('myMessage') ->atPath('property.path.name') ->setParameter('{{ value }}', '"Foo"') - ->setInvalidValue('Foo') + ->setInvalidValue($entity2) ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) ->assertRaised(); } @@ -204,7 +216,7 @@ public function testValidateCustomErrorPath() $this->buildViolation('myMessage') ->atPath('property.path.bar') ->setParameter('{{ value }}', '"Foo"') - ->setInvalidValue('Foo') + ->setInvalidValue($entity2) ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) ->assertRaised(); } @@ -229,7 +241,7 @@ public function testValidateUniquenessWithNull() $this->assertNoViolation(); } - public function testValidateUniquenessWithIgnoreNull() + public function testValidateUniquenessWithIgnoreNullDisabled() { $constraint = new UniqueEntity(array( 'message' => 'myMessage', @@ -262,6 +274,51 @@ public function testValidateUniquenessWithIgnoreNull() ->assertRaised(); } + /** + * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException + */ + public function testAllConfiguredFieldsAreCheckedOfBeingMappedByDoctrineWithIgnoreNullEnabled() + { + $constraint = new UniqueEntity(array( + 'message' => 'myMessage', + 'fields' => array('name', 'name2'), + 'em' => self::EM_NAME, + 'ignoreNull' => true, + )); + + $entity1 = new SingleIntIdEntity(1, null); + + $this->validator->validate($entity1, $constraint); + } + + public function testNoValidationIfFirstFieldIsNullAndNullValuesAreIgnored() + { + $constraint = new UniqueEntity(array( + 'message' => 'myMessage', + 'fields' => array('name', 'name2'), + 'em' => self::EM_NAME, + 'ignoreNull' => true, + )); + + $entity1 = new DoubleNullableNameEntity(1, null, 'Foo'); + $entity2 = new DoubleNullableNameEntity(2, null, 'Foo'); + + $this->validator->validate($entity1, $constraint); + + $this->assertNoViolation(); + + $this->em->persist($entity1); + $this->em->flush(); + + $this->validator->validate($entity1, $constraint); + + $this->assertNoViolation(); + + $this->validator->validate($entity2, $constraint); + + $this->assertNoViolation(); + } + public function testValidateUniquenessWithValidCustomErrorPath() { $constraint = new UniqueEntity(array( @@ -423,7 +480,7 @@ public function testAssociatedEntity() $this->buildViolation('myMessage') ->atPath('property.path.single') - ->setParameter('{{ value }}', $entity1) + ->setParameter('{{ value }}', 'object("Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity") identified by (id => 1)') ->setInvalidValue($entity1) ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) ->assertRaised(); @@ -456,12 +513,12 @@ public function testValidateUniquenessNotToStringEntityWithAssociatedEntity() $this->validator->validate($associated2, $constraint); - $expectedValue = 'Object of class "Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity2" identified by "2"'; + $expectedValue = 'object("Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdNoToStringEntity") identified by (id => 1)'; $this->buildViolation('myMessage') ->atPath('property.path.single') - ->setParameter('{{ value }}', '"'.$expectedValue.'"') - ->setInvalidValue($expectedValue) + ->setParameter('{{ value }}', $expectedValue) + ->setInvalidValue($entity1) ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) ->assertRaised(); } @@ -617,4 +674,65 @@ public function testInvalidateRepositoryForInheritance() $entity = new Person(1, 'Foo'); $this->validator->validate($entity, $constraint); } + + public function testValidateUniquenessWithCompositeObjectNoToStringIdEntity() + { + $constraint = new UniqueEntity(array( + 'message' => 'myMessage', + 'fields' => array('objectOne', 'objectTwo'), + 'em' => self::EM_NAME, + )); + + $objectOne = new SingleIntIdNoToStringEntity(1, 'foo'); + $objectTwo = new SingleIntIdNoToStringEntity(2, 'bar'); + + $this->em->persist($objectOne); + $this->em->persist($objectTwo); + $this->em->flush(); + + $entity = new CompositeObjectNoToStringIdEntity($objectOne, $objectTwo); + + $this->em->persist($entity); + $this->em->flush(); + + $newEntity = new CompositeObjectNoToStringIdEntity($objectOne, $objectTwo); + + $this->validator->validate($newEntity, $constraint); + + $expectedValue = 'object("Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdNoToStringEntity") identified by (id => 1)'; + + $this->buildViolation('myMessage') + ->atPath('property.path.objectOne') + ->setParameter('{{ value }}', $expectedValue) + ->setInvalidValue($objectOne) + ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) + ->assertRaised(); + } + + public function testValidateUniquenessWithCustomDoctrineTypeValue() + { + $constraint = new UniqueEntity(array( + 'message' => 'myMessage', + 'fields' => array('name'), + 'em' => self::EM_NAME, + )); + + $existingEntity = new SingleIntIdStringWrapperNameEntity(1, new StringWrapper('foo')); + + $this->em->persist($existingEntity); + $this->em->flush(); + + $newEntity = new SingleIntIdStringWrapperNameEntity(2, new StringWrapper('foo')); + + $this->validator->validate($newEntity, $constraint); + + $expectedValue = 'object("Symfony\Bridge\Doctrine\Tests\Fixtures\Type\StringWrapper")'; + + $this->buildViolation('myMessage') + ->atPath('property.path.name') + ->setParameter('{{ value }}', $expectedValue) + ->setInvalidValue($existingEntity->name) + ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) + ->assertRaised(); + } } diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php index 9867a2779f61a..c2fcb520e10fe 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php @@ -12,6 +12,8 @@ namespace Symfony\Bridge\Doctrine\Validator\Constraints; use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\ObjectManager; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\UnexpectedTypeException; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -79,17 +81,25 @@ public function validate($entity, Constraint $constraint) /* @var $class \Doctrine\Common\Persistence\Mapping\ClassMetadata */ $criteria = array(); + $hasNullValue = false; + foreach ($fields as $fieldName) { if (!$class->hasField($fieldName) && !$class->hasAssociation($fieldName)) { throw new ConstraintDefinitionException(sprintf('The field "%s" is not mapped by Doctrine, so it cannot be validated for uniqueness.', $fieldName)); } - $criteria[$fieldName] = $class->reflFields[$fieldName]->getValue($entity); + $fieldValue = $class->reflFields[$fieldName]->getValue($entity); + + if (null === $fieldValue) { + $hasNullValue = true; + } - if ($constraint->ignoreNull && null === $criteria[$fieldName]) { - return; + if ($constraint->ignoreNull && null === $fieldValue) { + continue; } + $criteria[$fieldName] = $fieldValue; + if (null !== $criteria[$fieldName] && $class->hasAssociation($fieldName)) { /* Ensure the Proxy is initialized before using reflection to * read its identifiers. This is necessary because the wrapped @@ -99,6 +109,17 @@ public function validate($entity, Constraint $constraint) } } + // validation doesn't fail if one of the fields is null and if null values should be ignored + if ($hasNullValue && $constraint->ignoreNull) { + return; + } + + // skip validation if there are no criteria (this can happen when the + // "ignoreNull" option is enabled and fields to be checked are null + if (empty($criteria)) { + return; + } + if (null !== $constraint->entityClass) { /* Retrieve repository from given entity name. * We ensure the retrieved repository can handle the entity @@ -141,15 +162,47 @@ public function validate($entity, Constraint $constraint) $errorPath = null !== $constraint->errorPath ? $constraint->errorPath : $fields[0]; $invalidValue = isset($criteria[$errorPath]) ? $criteria[$errorPath] : $criteria[$fields[0]]; - if (is_object($invalidValue) && !method_exists($invalidValue, '__toString')) { - $invalidValue = sprintf('Object of class "%s" identified by "%s"', get_class($entity), implode(', ', $class->getIdentifierValues($entity))); - } - $this->context->buildViolation($constraint->message) ->atPath($errorPath) - ->setParameter('{{ value }}', $this->formatValue($invalidValue, static::OBJECT_TO_STRING | static::PRETTY_DATE)) + ->setParameter('{{ value }}', $this->formatWithIdentifiers($em, $class, $invalidValue)) ->setInvalidValue($invalidValue) ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) ->addViolation(); } + + private function formatWithIdentifiers(ObjectManager $em, ClassMetadata $class, $value) + { + if (!is_object($value) || $value instanceof \DateTimeInterface) { + return $this->formatValue($value, self::PRETTY_DATE); + } + + if ($class->getName() !== $idClass = get_class($value)) { + // non unique value might be a composite PK that consists of other entity objects + if ($em->getMetadataFactory()->hasMetadataFor($idClass)) { + $identifiers = $em->getClassMetadata($idClass)->getIdentifierValues($value); + } else { + // this case might happen if the non unique column has a custom doctrine type and its value is an object + // in which case we cannot get any identifiers for it + $identifiers = array(); + } + } else { + $identifiers = $class->getIdentifierValues($value); + } + + if (!$identifiers) { + return sprintf('object("%s")', $idClass); + } + + array_walk($identifiers, function (&$id, $field) { + if (!is_object($id) || $id instanceof \DateTimeInterface) { + $idAsString = $this->formatValue($id, self::PRETTY_DATE); + } else { + $idAsString = sprintf('object("%s")', get_class($id)); + } + + $id = sprintf('%s => %s', $field, $idAsString); + }); + + return sprintf('object("%s") identified by (%s)', $idClass, implode(', ', $identifiers)); + } } diff --git a/src/Symfony/Bridge/Doctrine/composer.json b/src/Symfony/Bridge/Doctrine/composer.json index e2803b0168cf3..67854035ce6b8 100644 --- a/src/Symfony/Bridge/Doctrine/composer.json +++ b/src/Symfony/Bridge/Doctrine/composer.json @@ -22,19 +22,23 @@ }, "require-dev": { "symfony/stopwatch": "~2.8|~3.0", - "symfony/dependency-injection": "~2.8|~3.0", - "symfony/form": "~3.0,>=3.0.5", + "symfony/dependency-injection": "~3.3", + "symfony/form": "^3.2.5", "symfony/http-kernel": "~2.8|~3.0", "symfony/property-access": "~2.8|~3.0", "symfony/property-info": "~2.8|3.0", "symfony/proxy-manager-bridge": "~2.8|~3.0", "symfony/security": "~2.8|~3.0", "symfony/expression-language": "~2.8|~3.0", - "symfony/validator": "~2.8|~3.0", + "symfony/validator": "^2.8.18|^3.2.5", "symfony/translation": "~2.8|~3.0", "doctrine/data-fixtures": "1.0.*", "doctrine/dbal": "~2.4", - "doctrine/orm": "~2.4,>=2.4.5" + "doctrine/orm": "^2.4.5" + }, + "conflict": { + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", + "symfony/dependency-injection": "<3.3" }, "suggest": { "symfony/form": "", @@ -53,7 +57,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.3-dev" } } } diff --git a/src/Symfony/Bridge/Doctrine/phpunit.xml.dist b/src/Symfony/Bridge/Doctrine/phpunit.xml.dist index c006d232219a4..24f92f2ab8e2f 100644 --- a/src/Symfony/Bridge/Doctrine/phpunit.xml.dist +++ b/src/Symfony/Bridge/Doctrine/phpunit.xml.dist @@ -5,6 +5,8 @@ backupGlobals="false" colors="true" bootstrap="vendor/autoload.php" + failOnRisky="true" + failOnWarning="true" > diff --git a/src/Symfony/Bridge/Monolog/CHANGELOG.md b/src/Symfony/Bridge/Monolog/CHANGELOG.md index cb7deea2e9052..f91d4c5d9a224 100644 --- a/src/Symfony/Bridge/Monolog/CHANGELOG.md +++ b/src/Symfony/Bridge/Monolog/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +3.3.0 +----- + + * Improved the console handler output formatting by adding var-dumper support + 3.0.0 ----- diff --git a/src/Symfony/Bridge/Monolog/Formatter/ConsoleFormatter.php b/src/Symfony/Bridge/Monolog/Formatter/ConsoleFormatter.php index 1af93bc97aebe..9b372d6e22749 100644 --- a/src/Symfony/Bridge/Monolog/Formatter/ConsoleFormatter.php +++ b/src/Symfony/Bridge/Monolog/Formatter/ConsoleFormatter.php @@ -11,24 +11,102 @@ namespace Symfony\Bridge\Monolog\Formatter; -use Monolog\Formatter\LineFormatter; +use Monolog\Formatter\FormatterInterface; use Monolog\Logger; +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\VarDumper\Cloner\Data; +use Symfony\Component\VarDumper\Cloner\Stub; +use Symfony\Component\VarDumper\Cloner\VarCloner; +use Symfony\Component\VarDumper\Dumper\CliDumper; /** * Formats incoming records for console output by coloring them depending on log level. * * @author Tobias Schultze + * @author Grégoire Pineau */ -class ConsoleFormatter extends LineFormatter +class ConsoleFormatter implements FormatterInterface { - const SIMPLE_FORMAT = "%start_tag%[%datetime%] %channel%.%level_name%:%end_tag% %message% %context% %extra%\n"; + const SIMPLE_FORMAT = "%datetime% %start_tag%%level_name%%end_tag% [%channel%] %message%%context%%extra%\n"; + const SIMPLE_DATE = 'H:i:s'; + + private static $levelColorMap = array( + Logger::DEBUG => 'fg=white', + Logger::INFO => 'fg=green', + Logger::NOTICE => 'fg=blue', + Logger::WARNING => 'fg=cyan', + Logger::ERROR => 'fg=yellow', + Logger::CRITICAL => 'fg=red', + Logger::ALERT => 'fg=red', + Logger::EMERGENCY => 'fg=white;bg=red', + ); + + private $options; + private $cloner; + private $outputBuffer; + private $dumper; + + /** + * Constructor. + * + * Available options: + * * format: The format of the outputted log string. The following placeholders are supported: %datetime%, %start_tag%, %level_name%, %end_tag%, %channel%, %message%, %context%, %extra%; + * * date_format: The format of the outputted date string; + * * colors: If true, the log string contains ANSI code to add color; + * * multiline: If false, "context" and "extra" are dumped on one line. + */ + public function __construct($options = array()) + { + // BC Layer + if (!is_array($options)) { + @trigger_error(sprintf('The constructor arguments $format, $dateFormat, $allowInlineLineBreaks, $ignoreEmptyContextAndExtra of "%s" are deprecated since 3.3 and will be removed in 4.0. Use $options instead.', self::class), E_USER_DEPRECATED); + $args = func_get_args(); + $options = array(); + if (isset($args[0])) { + $options['format'] = $args[0]; + } + if (isset($args[1])) { + $options['date_format'] = $args[1]; + } + if (isset($args[2])) { + $options['multiline'] = $args[2]; + } + } + + $this->options = array_replace(array( + 'format' => self::SIMPLE_FORMAT, + 'date_format' => self::SIMPLE_DATE, + 'colors' => true, + 'multiline' => false, + ), $options); + + if (class_exists(VarCloner::class)) { + $this->cloner = new VarCloner(); + $this->cloner->addCasters(array( + '*' => array($this, 'castObject'), + )); + + $this->outputBuffer = fopen('php://memory', 'r+b'); + if ($this->options['multiline']) { + $output = $this->outputBuffer; + } else { + $output = array($this, 'echoLine'); + } + + $this->dumper = new CliDumper($output, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR); + } + } /** * {@inheritdoc} */ - public function __construct($format = null, $dateFormat = null, $allowInlineLineBreaks = false, $ignoreEmptyContextAndExtra = true) + public function formatBatch(array $records) { - parent::__construct($format, $dateFormat, $allowInlineLineBreaks, $ignoreEmptyContextAndExtra); + foreach ($records as $key => $record) { + $records[$key] = $this->format($record); + } + + return $records; } /** @@ -36,20 +114,104 @@ public function __construct($format = null, $dateFormat = null, $allowInlineLine */ public function format(array $record) { - if ($record['level'] >= Logger::ERROR) { - $record['start_tag'] = ''; - $record['end_tag'] = ''; - } elseif ($record['level'] >= Logger::NOTICE) { - $record['start_tag'] = ''; - $record['end_tag'] = ''; - } elseif ($record['level'] >= Logger::INFO) { - $record['start_tag'] = ''; - $record['end_tag'] = ''; + $record = $this->replacePlaceHolder($record); + + $levelColor = self::$levelColorMap[$record['level']]; + + if ($this->options['multiline']) { + $context = $extra = "\n"; + } else { + $context = $extra = ' '; + } + $context .= $this->dumpData($record['context']); + $extra .= $this->dumpData($record['extra']); + + $formatted = strtr($this->options['format'], array( + '%datetime%' => $record['datetime']->format($this->options['date_format']), + '%start_tag%' => sprintf('<%s>', $levelColor), + '%level_name%' => sprintf('%-9s', $record['level_name']), + '%end_tag%' => '', + '%channel%' => $record['channel'], + '%message%' => $this->replacePlaceHolder($record)['message'], + '%context%' => $context, + '%extra%' => $extra, + )); + + return $formatted; + } + + /** + * @internal + */ + public function echoLine($line, $depth, $indentPad) + { + if (-1 !== $depth) { + fwrite($this->outputBuffer, $line); + } + } + + /** + * @internal + */ + public function castObject($v, array $a, Stub $s, $isNested) + { + if ($this->options['multiline']) { + return $a; + } + + if ($isNested && !$v instanceof \DateTimeInterface) { + $s->cut = -1; + $a = array(); + } + + return $a; + } + + private function replacePlaceHolder(array $record) + { + $message = $record['message']; + + if (false === strpos($message, '{')) { + return $record; + } + + $context = $record['context']; + + $replacements = array(); + foreach ($context as $k => $v) { + // Remove quotes added by the dumper around string. + $v = trim($this->dumpData($v, false), '"'); + $v = OutputFormatter::escape($v); + $replacements['{'.$k.'}'] = sprintf('%s', $v); + } + + $record['message'] = strtr($message, $replacements); + + return $record; + } + + private function dumpData($data, $colors = null) + { + if (null === $this->dumper) { + return ''; + } + + if (null === $colors) { + $this->dumper->setColors($this->options['colors']); } else { - $record['start_tag'] = ''; - $record['end_tag'] = ''; + $this->dumper->setColors($colors); } - return parent::format($record); + if (!$data instanceof Data) { + $data = $this->cloner->cloneVar($data); + } + $data = $data->withRefHandles(false); + $this->dumper->dump($data); + + $dump = stream_get_contents($this->outputBuffer, -1, 0); + rewind($this->outputBuffer); + ftruncate($this->outputBuffer, 0); + + return rtrim($dump); } } diff --git a/src/Symfony/Bridge/Monolog/Formatter/VarDumperFormatter.php b/src/Symfony/Bridge/Monolog/Formatter/VarDumperFormatter.php new file mode 100644 index 0000000000000..e96b510a8bb3f --- /dev/null +++ b/src/Symfony/Bridge/Monolog/Formatter/VarDumperFormatter.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Monolog\Formatter; + +use Monolog\Formatter\FormatterInterface; +use Symfony\Component\VarDumper\Cloner\VarCloner; + +/** + * @author Grégoire Pineau + */ +class VarDumperFormatter implements FormatterInterface +{ + private $cloner; + + public function __construct(VarCloner $cloner = null) + { + $this->cloner = $cloner ?: new VarCloner(); + } + + public function format(array $record) + { + $record['context'] = $this->cloner->cloneVar($record['context']); + $record['extra'] = $this->cloner->cloneVar($record['extra']); + + return $record; + } + + public function formatBatch(array $records) + { + foreach ($records as $k => $record) { + $record[$k] = $this->format($record); + } + + return $records; + } +} diff --git a/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php b/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php index 2636bc3b8980e..e78c9d0c9d622 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('{\b(?:Chrome/\d+(?:\.\d+)*|Firefox/(?:4[3-9]|[5-9]\d|\d{3,})(?:\.\d)*)\b}', $event->getRequest()->headers->get('User-Agent'))) { + if (!preg_match(static::USER_AGENT_REGEX, $event->getRequest()->headers->get('User-Agent'))) { $this->sendHeaders = false; $this->headers = array(); diff --git a/src/Symfony/Bridge/Monolog/Handler/ConsoleHandler.php b/src/Symfony/Bridge/Monolog/Handler/ConsoleHandler.php index 24c79397cf9a2..01f055f20a1aa 100644 --- a/src/Symfony/Bridge/Monolog/Handler/ConsoleHandler.php +++ b/src/Symfony/Bridge/Monolog/Handler/ConsoleHandler.php @@ -164,7 +164,14 @@ protected function write(array $record) */ protected function getDefaultFormatter() { - return new ConsoleFormatter(); + if (!$this->output) { + return new ConsoleFormatter(); + } + + return new ConsoleFormatter(array( + 'colors' => $this->output->isDecorated(), + 'multiline' => OutputInterface::VERBOSITY_DEBUG <= $this->output->getVerbosity(), + )); } /** diff --git a/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php b/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php new file mode 100644 index 0000000000000..599940e8d7714 --- /dev/null +++ b/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php @@ -0,0 +1,114 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Monolog\Handler; + +use Monolog\Handler\AbstractHandler; +use Monolog\Logger; +use Symfony\Bridge\Monolog\Formatter\VarDumperFormatter; + +/** + * @author Grégoire Pineau + */ +class ServerLogHandler extends AbstractHandler +{ + private $host; + private $context; + private $socket; + + public function __construct($host, $level = Logger::DEBUG, $bubble = true, $context = array()) + { + parent::__construct($level, $bubble); + + if (false === strpos($host, '://')) { + $host = 'tcp://'.$host; + } + + $this->host = $host; + $this->context = stream_context_create($context); + } + + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if (!$this->isHandling($record)) { + return false; + } + + set_error_handler(self::class.'::nullErrorHandler'); + + try { + if (!$this->socket = $this->socket ?: $this->createSocket()) { + return false === $this->bubble; + } + + $recordFormatted = $this->formatRecord($record); + + if (!fwrite($this->socket, $recordFormatted)) { + fclose($this->socket); + + // Let's retry: the persistent connection might just be stale + if ($this->socket = $this->createSocket()) { + fwrite($this->socket, $recordFormatted); + } + } + } finally { + restore_error_handler(); + } + + return false === $this->bubble; + } + + /** + * {@inheritdoc} + */ + protected function getDefaultFormatter() + { + return new VarDumperFormatter(); + } + + private static function nullErrorHandler() + { + } + + private function createSocket() + { + $socket = stream_socket_client($this->host, $errno, $errstr, 0, STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT | STREAM_CLIENT_PERSISTENT, $this->context); + + if ($socket) { + stream_set_blocking($socket, false); + } + + return $socket; + } + + private function formatRecord(array $record) + { + if ($this->processors) { + foreach ($this->processors as $processor) { + $record = call_user_func($processor, $record); + } + } + + $recordFormatted = $this->getFormatter()->format($record); + + foreach (array('log_uuid', 'uuid', 'uid') as $key) { + if (isset($record['extra'][$key])) { + $recordFormatted['log_id'] = $record['extra'][$key]; + break; + } + } + + return base64_encode(serialize($recordFormatted))."\n"; + } +} diff --git a/src/Symfony/Bridge/Monolog/LICENSE b/src/Symfony/Bridge/Monolog/LICENSE index 12a74531e40a4..17d16a13367dd 100644 --- a/src/Symfony/Bridge/Monolog/LICENSE +++ b/src/Symfony/Bridge/Monolog/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2016 Fabien Potencier +Copyright (c) 2004-2017 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bridge/Monolog/Tests/Handler/ConsoleHandlerTest.php b/src/Symfony/Bridge/Monolog/Tests/Handler/ConsoleHandlerTest.php index 713171cbb95d7..63b5a8f07b6d7 100644 --- a/src/Symfony/Bridge/Monolog/Tests/Handler/ConsoleHandlerTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/Handler/ConsoleHandlerTest.php @@ -12,6 +12,7 @@ namespace Symfony\Bridge\Monolog\Tests\Handler; use Monolog\Logger; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Monolog\Handler\ConsoleHandler; use Symfony\Component\Console\ConsoleEvents; use Symfony\Component\Console\Event\ConsoleCommandEvent; @@ -26,7 +27,7 @@ * * @author Tobias Schultze */ -class ConsoleHandlerTest extends \PHPUnit_Framework_TestCase +class ConsoleHandlerTest extends TestCase { public function testConstructor() { @@ -45,7 +46,7 @@ public function testIsHandling() */ public function testVerbosityMapping($verbosity, $level, $isHandling, array $map = array()) { - $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + $output = $this->getMockBuilder('Symfony\Component\Console\Output\OutputInterface')->getMock(); $output ->expects($this->atLeastOnce()) ->method('getVerbosity') @@ -58,13 +59,19 @@ public function testVerbosityMapping($verbosity, $level, $isHandling, array $map // check that the handler actually outputs the record if it handles it $levelName = Logger::getLevelName($level); + $levelName = sprintf('%-9s', $levelName); - $realOutput = $this->getMock('Symfony\Component\Console\Output\Output', array('doWrite')); + $realOutput = $this->getMockBuilder('Symfony\Component\Console\Output\Output')->setMethods(array('doWrite'))->getMock(); $realOutput->setVerbosity($verbosity); + if ($realOutput->isDebug()) { + $log = "16:21:54 $levelName [app] My info message\n[]\n[]\n"; + } else { + $log = "16:21:54 $levelName [app] My info message [] []\n"; + } $realOutput ->expects($isHandling ? $this->once() : $this->never()) ->method('doWrite') - ->with("[2013-05-29 16:21:54] app.$levelName: My info message \n", false); + ->with($log, false); $handler = new ConsoleHandler($realOutput, true, $map); $infoRecord = array( @@ -103,7 +110,7 @@ public function provideVerbosityMappingTests() public function testVerbosityChanged() { - $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + $output = $this->getMockBuilder('Symfony\Component\Console\Output\OutputInterface')->getMock(); $output ->expects($this->at(0)) ->method('getVerbosity') @@ -133,7 +140,7 @@ public function testGetFormatter() public function testWritingAndFormatting() { - $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + $output = $this->getMockBuilder('Symfony\Component\Console\Output\OutputInterface')->getMock(); $output ->expects($this->any()) ->method('getVerbosity') @@ -142,7 +149,7 @@ public function testWritingAndFormatting() $output ->expects($this->once()) ->method('write') - ->with('[2013-05-29 16:21:54] app.INFO: My info message '."\n") + ->with("16:21:54 INFO [app] My info message\n[]\n[]\n") ; $handler = new ConsoleHandler(null, false); @@ -188,12 +195,12 @@ public function testLogsFromListeners() $logger->addInfo('After terminate message.'); }); - $event = new ConsoleCommandEvent(new Command('foo'), $this->getMock('Symfony\Component\Console\Input\InputInterface'), $output); + $event = new ConsoleCommandEvent(new Command('foo'), $this->getMockBuilder('Symfony\Component\Console\Input\InputInterface')->getMock(), $output); $dispatcher->dispatch(ConsoleEvents::COMMAND, $event); $this->assertContains('Before command message.', $out = $output->fetch()); $this->assertContains('After command message.', $out); - $event = new ConsoleTerminateEvent(new Command('foo'), $this->getMock('Symfony\Component\Console\Input\InputInterface'), $output, 0); + $event = new ConsoleTerminateEvent(new Command('foo'), $this->getMockBuilder('Symfony\Component\Console\Input\InputInterface')->getMock(), $output, 0); $dispatcher->dispatch(ConsoleEvents::TERMINATE, $event); $this->assertContains('Before terminate message.', $out = $output->fetch()); $this->assertContains('After terminate message.', $out); diff --git a/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php b/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php index 48bddc99a5eef..3c34f065eb038 100644 --- a/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php @@ -11,13 +11,14 @@ namespace Symfony\Bridge\Monolog\Tests\Handler\FingersCrossed; +use Monolog\Logger; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Monolog\Handler\FingersCrossed\NotFoundActivationStrategy; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpKernel\Exception\HttpException; -use Monolog\Logger; -class NotFoundActivationStrategyTest extends \PHPUnit_Framework_TestCase +class NotFoundActivationStrategyTest extends TestCase { /** * @dataProvider isActivatedProvider diff --git a/src/Symfony/Bridge/Monolog/Tests/LoggerTest.php b/src/Symfony/Bridge/Monolog/Tests/LoggerTest.php index 9597d01c96cb1..c24c7a4133baf 100644 --- a/src/Symfony/Bridge/Monolog/Tests/LoggerTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/LoggerTest.php @@ -12,11 +12,12 @@ namespace Symfony\Bridge\Monolog\Tests; use Monolog\Handler\TestHandler; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Monolog\Handler\DebugHandler; use Symfony\Bridge\Monolog\Processor\DebugProcessor; use Symfony\Bridge\Monolog\Logger; -class LoggerTest extends \PHPUnit_Framework_TestCase +class LoggerTest extends TestCase { /** * @group legacy diff --git a/src/Symfony/Bridge/Monolog/Tests/Processor/WebProcessorTest.php b/src/Symfony/Bridge/Monolog/Tests/Processor/WebProcessorTest.php index 143f63b5371b3..e73002e0292fa 100644 --- a/src/Symfony/Bridge/Monolog/Tests/Processor/WebProcessorTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/Processor/WebProcessorTest.php @@ -12,10 +12,11 @@ namespace Symfony\Bridge\Monolog\Tests\Processor; use Monolog\Logger; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Monolog\Processor\WebProcessor; use Symfony\Component\HttpFoundation\Request; -class WebProcessorTest extends \PHPUnit_Framework_TestCase +class WebProcessorTest extends TestCase { public function testUsesRequestServerData() { @@ -35,7 +36,7 @@ public function testUsesRequestServerData() public function testUseRequestClientIp() { - Request::setTrustedProxies(array('192.168.0.1')); + Request::setTrustedProxies(array('192.168.0.1'), Request::HEADER_X_FORWARDED_ALL); list($event, $server) = $this->createRequestEvent(array('X_FORWARDED_FOR' => '192.168.0.2')); $processor = new WebProcessor(); diff --git a/src/Symfony/Bridge/Monolog/composer.json b/src/Symfony/Bridge/Monolog/composer.json index be0c3a529b46a..d60949aab872c 100644 --- a/src/Symfony/Bridge/Monolog/composer.json +++ b/src/Symfony/Bridge/Monolog/composer.json @@ -17,12 +17,16 @@ ], "require": { "php": ">=5.5.9", - "monolog/monolog": "~1.11", + "monolog/monolog": "~1.19", "symfony/http-kernel": "~2.8|~3.0" }, "require-dev": { "symfony/console": "~2.8|~3.0", - "symfony/event-dispatcher": "~2.8|~3.0" + "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/var-dumper": "~3.3" + }, + "conflict": { + "symfony/http-foundation": "<3.3" }, "suggest": { "symfony/http-kernel": "For using the debugging handlers together with the response life cycle of the HTTP kernel.", @@ -38,7 +42,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.3-dev" } } } diff --git a/src/Symfony/Bridge/Monolog/phpunit.xml.dist b/src/Symfony/Bridge/Monolog/phpunit.xml.dist index 8a60f06a7a310..28905908b1897 100644 --- a/src/Symfony/Bridge/Monolog/phpunit.xml.dist +++ b/src/Symfony/Bridge/Monolog/phpunit.xml.dist @@ -5,6 +5,8 @@ backupGlobals="false" colors="true" bootstrap="vendor/autoload.php" + failOnRisky="true" + failOnWarning="true" > diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php index ca85d8aba5994..4279995ec91ca 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php @@ -19,6 +19,7 @@ class DeprecationErrorHandler { const MODE_WEAK = 'weak'; + const MODE_WEAK_VENDORS = 'weak_vendors'; const MODE_DISABLED = 'disabled'; private static $isRegistered = false; @@ -28,6 +29,7 @@ class DeprecationErrorHandler * * The following reporting modes are supported: * - use "weak" to hide the deprecation report but keep a global count; + * - use "weak_vendors" to act as "weak" but only for vendors; * - use "/some-regexp/" to stop the test suite whenever a deprecation * message matches the given regular expression; * - use a number to define the upper bound of allowed deprecations, @@ -41,6 +43,8 @@ public static function register($mode = 0) return; } + $UtilPrefix = class_exists('PHPUnit_Util_ErrorHandler') ? 'PHPUnit_Util_' : 'PHPUnit\Util\\'; + $getMode = function () use ($mode) { static $memoizedMode = false; @@ -50,40 +54,71 @@ public static function register($mode = 0) if (false === $mode) { $mode = getenv('SYMFONY_DEPRECATIONS_HELPER'); } - if (DeprecationErrorHandler::MODE_WEAK !== $mode && (!isset($mode[0]) || '/' !== $mode[0])) { + if (DeprecationErrorHandler::MODE_WEAK !== $mode && DeprecationErrorHandler::MODE_WEAK_VENDORS !== $mode && (!isset($mode[0]) || '/' !== $mode[0])) { $mode = preg_match('/^[1-9][0-9]*$/', $mode) ? (int) $mode : 0; } return $memoizedMode = $mode; }; + $inVendors = function ($path) { + /** @var string[] absolute paths to vendor directories */ + static $vendors; + if (null === $vendors) { + foreach (get_declared_classes() as $class) { + if ('C' === $class[0] && 0 === strpos($class, 'ComposerAutoloaderInit')) { + $r = new \ReflectionClass($class); + $v = dirname(dirname($r->getFileName())); + if (file_exists($v.'/composer/installed.json')) { + $vendors[] = $v; + } + } + } + } + $path = realpath($path) ?: $path; + foreach ($vendors as $vendor) { + if (0 === strpos($path, $vendor) && false !== strpbrk(substr($path, strlen($vendor), 1), '/'.DIRECTORY_SEPARATOR)) { + return true; + } + } + + return false; + }; + $deprecations = array( 'unsilencedCount' => 0, 'remainingCount' => 0, 'legacyCount' => 0, 'otherCount' => 0, + 'remaining vendorCount' => 0, 'unsilenced' => array(), 'remaining' => array(), 'legacy' => array(), 'other' => array(), + 'remaining vendor' => array(), ); - $deprecationHandler = function ($type, $msg, $file, $line, $context) use (&$deprecations, $getMode) { + $deprecationHandler = function ($type, $msg, $file, $line, $context) use (&$deprecations, $getMode, $UtilPrefix, $inVendors) { $mode = $getMode(); if ((E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) || DeprecationErrorHandler::MODE_DISABLED === $mode) { - return \PHPUnit_Util_ErrorHandler::handleError($type, $msg, $file, $line, $context); + $ErrorHandler = $UtilPrefix.'ErrorHandler'; + + return $ErrorHandler::handleError($type, $msg, $file, $line, $context); } $trace = debug_backtrace(true); $group = 'other'; + $isWeak = DeprecationErrorHandler::MODE_WEAK === $mode || (DeprecationErrorHandler::MODE_WEAK_VENDORS === $mode && $isVendor = $inVendors($file)); + $i = count($trace); - while (1 < $i && (!isset($trace[--$i]['class']) || ('ReflectionMethod' === $trace[$i]['class'] || 0 === strpos($trace[$i]['class'], 'PHPUnit_')))) { + while (1 < $i && (!isset($trace[--$i]['class']) || ('ReflectionMethod' === $trace[$i]['class'] || 0 === strpos($trace[$i]['class'], 'PHPUnit_') || 0 === strpos($trace[$i]['class'], 'PHPUnit\\')))) { // No-op } if (isset($trace[$i]['object']) || isset($trace[$i]['class'])) { $class = isset($trace[$i]['object']) ? get_class($trace[$i]['object']) : $trace[$i]['class']; $method = $trace[$i]['function']; + $Test = $UtilPrefix.'Test'; if (0 !== error_reporting()) { $group = 'unsilenced'; @@ -91,9 +126,11 @@ public static function register($mode = 0) || 0 === strpos($method, 'provideLegacy') || 0 === strpos($method, 'getLegacy') || strpos($class, '\Legacy') - || in_array('legacy', \PHPUnit_Util_Test::getGroups($class, $method), true) + || in_array('legacy', $Test::getGroups($class, $method), true) ) { $group = 'legacy'; + } elseif (DeprecationErrorHandler::MODE_WEAK_VENDORS === $mode && $isVendor) { + $group = 'remaining vendor'; } else { $group = 'remaining'; } @@ -112,13 +149,13 @@ public static function register($mode = 0) exit(1); } - if ('legacy' !== $group && DeprecationErrorHandler::MODE_WEAK !== $mode) { + if ('legacy' !== $group && !$isWeak) { $ref = &$deprecations[$group][$msg]['count']; ++$ref; $ref = &$deprecations[$group][$msg][$class.'::'.$method]; ++$ref; } - } elseif (DeprecationErrorHandler::MODE_WEAK !== $mode) { + } elseif (!$isWeak) { $ref = &$deprecations[$group][$msg]['count']; ++$ref; } @@ -128,7 +165,7 @@ public static function register($mode = 0) if (null !== $oldErrorHandler) { restore_error_handler(); - if (array('PHPUnit_Util_ErrorHandler', 'handleError') === $oldErrorHandler) { + if (array($UtilPrefix.'ErrorHandler', 'handleError') === $oldErrorHandler) { restore_error_handler(); self::register($mode); } @@ -141,7 +178,7 @@ public static function register($mode = 0) return "\x1B[{$color}m{$str}\x1B[0m"; }; } else { - $colorize = function ($str) {return $str;}; + $colorize = function ($str) { return $str; }; } register_shutdown_function(function () use ($getMode, &$deprecations, $deprecationHandler, $colorize) { $mode = $getMode(); @@ -152,7 +189,7 @@ public static function register($mode = 0) restore_error_handler(); if (DeprecationErrorHandler::MODE_WEAK === $mode) { - $colorize = function ($str) {return $str;}; + $colorize = function ($str) { return $str; }; } if ($currErrorHandler !== $deprecationHandler) { echo "\n", $colorize('THE ERROR HANDLER HAS CHANGED!', true), "\n"; @@ -162,9 +199,18 @@ public static function register($mode = 0) return $b['count'] - $a['count']; }; - foreach (array('unsilenced', 'remaining', 'legacy', 'other') as $group) { + $groups = array('unsilenced', 'remaining'); + if (DeprecationErrorHandler::MODE_WEAK_VENDORS === $mode) { + $groups[] = 'remaining vendor'; + } + array_push($groups, 'legacy', 'other'); + + foreach ($groups as $group) { if ($deprecations[$group.'Count']) { - echo "\n", $colorize(sprintf('%s deprecation notices (%d)', ucfirst($group), $deprecations[$group.'Count']), 'legacy' !== $group), "\n"; + echo "\n", $colorize( + sprintf('%s deprecation notices (%d)', ucfirst($group), $deprecations[$group.'Count']), + 'legacy' !== $group && 'remaining vendor' !== $group + ), "\n"; uasort($deprecations[$group], $cmp); diff --git a/src/Symfony/Bridge/PhpUnit/LICENSE b/src/Symfony/Bridge/PhpUnit/LICENSE index 39fa189d2b5fc..207646a052dcd 100644 --- a/src/Symfony/Bridge/PhpUnit/LICENSE +++ b/src/Symfony/Bridge/PhpUnit/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014-2016 Fabien Potencier +Copyright (c) 2014-2017 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/Command.php b/src/Symfony/Bridge/PhpUnit/Legacy/Command.php new file mode 100644 index 0000000000000..0aec8ab67f33e --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Legacy/Command.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit\Legacy; + +/** + * {@inheritdoc} + * + * @internal + */ +class Command extends \PHPUnit_TextUI_Command +{ + /** + * {@inheritdoc} + */ + protected function createRunner() + { + return new TestRunner($this->arguments['loader']); + } +} diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListener.php b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListener.php new file mode 100644 index 0000000000000..5ed545a5127cc --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListener.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit\Legacy; + +/** + * Collects and replays skipped tests. + * + * @author Nicolas Grekas + * + * @internal + */ +class SymfonyTestsListener extends \PHPUnit_Framework_BaseTestListener +{ + private $trait; + + public function __construct(array $mockedNamespaces = array()) + { + $this->trait = new SymfonyTestsListenerTrait($mockedNamespaces); + } + + public function globalListenerDisabled() + { + $this->trait->globalListenerDisabled(); + } + + public function startTestSuite(\PHPUnit_Framework_TestSuite $suite) + { + return $this->trait->startTestSuite($suite); + } + + public function addSkippedTest(\PHPUnit_Framework_Test $test, \Exception $e, $time) + { + return $this->trait->addSkippedTest($test, $e, $time); + } + + public function startTest(\PHPUnit_Framework_Test $test) + { + return $this->trait->startTest($test); + } + + public function addWarning(\PHPUnit_Framework_Test $test, \PHPUnit_Framework_Warning $e, $time) + { + return $this->trait->addWarning($test, $e, $time); + } + + public function endTest(\PHPUnit_Framework_Test $test, $time) + { + return $this->trait->endTest($test, $time); + } +} diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php new file mode 100644 index 0000000000000..f8814c3c844a7 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php @@ -0,0 +1,319 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit\Legacy; + +use Doctrine\Common\Annotations\AnnotationRegistry; +use PHPUnit\Framework\AssertionFailedError; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Util\Blacklist; +use PHPUnit\Util\Test; +use Symfony\Bridge\PhpUnit\ClockMock; +use Symfony\Bridge\PhpUnit\DnsMock; + +/** + * PHP 5.3 compatible trait-like shared implementation. + * + * @author Nicolas Grekas + * + * @internal + */ +class SymfonyTestsListenerTrait +{ + private static $globallyEnabled = false; + private $state = -1; + private $skippedFile = false; + private $wasSkipped = array(); + private $isSkipped = array(); + private $expectedDeprecations = array(); + private $gatheredDeprecations = array(); + private $previousErrorHandler; + private $testsWithWarnings; + private $reportUselessTests; + private $error; + + /** + * @param array $mockedNamespaces List of namespaces, indexed by mocked features (time-sensitive or dns-sensitive) + */ + public function __construct(array $mockedNamespaces = array()) + { + if (class_exists('PHPUnit_Util_Blacklist')) { + \PHPUnit_Util_Blacklist::$blacklistedClassNames['\Symfony\Bridge\PhpUnit\DeprecationErrorHandler'] = 1; + \PHPUnit_Util_Blacklist::$blacklistedClassNames['\Symfony\Bridge\PhpUnit\SymfonyTestsListener'] = 1; + \PHPUnit_Util_Blacklist::$blacklistedClassNames['\Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListener'] = 1; + \PHPUnit_Util_Blacklist::$blacklistedClassNames['\Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait'] = 1; + } else { + Blacklist::$blacklistedClassNames['\Symfony\Bridge\PhpUnit\DeprecationErrorHandler'] = 1; + Blacklist::$blacklistedClassNames['\Symfony\Bridge\PhpUnit\SymfonyTestsListener'] = 1; + Blacklist::$blacklistedClassNames['\Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait'] = 1; + } + + $warn = false; + foreach ($mockedNamespaces as $type => $namespaces) { + if (!is_array($namespaces)) { + $namespaces = array($namespaces); + } + if (is_int($type)) { + // @deprecated BC with v2.8 to v3.0 + $type = 'time-sensitive'; + $warn = true; + } + if ('time-sensitive' === $type) { + foreach ($namespaces as $ns) { + ClockMock::register($ns.'\DummyClass'); + } + } + if ('dns-sensitive' === $type) { + foreach ($namespaces as $ns) { + DnsMock::register($ns.'\DummyClass'); + } + } + } + if (self::$globallyEnabled) { + $this->state = -2; + } else { + self::$globallyEnabled = true; + if ($warn) { + echo "Clock-mocked namespaces for SymfonyTestsListener need to be nested in a \"time-sensitive\" key. This will be enforced in Symfony 4.0.\n"; + } + } + } + + public function __destruct() + { + if (0 < $this->state) { + file_put_contents($this->skippedFile, 'isSkipped, true).';'); + } + } + + public function globalListenerDisabled() + { + self::$globallyEnabled = false; + $this->state = -1; + } + + public function startTestSuite($suite) + { + if (class_exists('PHPUnit_Util_Blacklist', false)) { + $Test = 'PHPUnit_Util_Test'; + } else { + $Test = 'PHPUnit\Util\Test'; + } + $suiteName = $suite->getName(); + $this->testsWithWarnings = array(); + + if (-1 === $this->state) { + echo "Testing $suiteName\n"; + $this->state = 0; + + if (!class_exists('Doctrine\Common\Annotations\AnnotationRegistry', false) && class_exists('Doctrine\Common\Annotations\AnnotationRegistry')) { + AnnotationRegistry::registerLoader('class_exists'); + } + + if ($this->skippedFile = getenv('SYMFONY_PHPUNIT_SKIPPED_TESTS')) { + $this->state = 1; + + if (file_exists($this->skippedFile)) { + $this->state = 2; + + if (!$this->wasSkipped = require $this->skippedFile) { + echo "All tests already ran successfully.\n"; + $suite->setTests(array()); + } + } + } + $testSuites = array($suite); + for ($i = 0; isset($testSuites[$i]); ++$i) { + foreach ($testSuites[$i]->tests() as $test) { + if ($test instanceof \PHPUnit_Framework_TestSuite || $test instanceof TestSuite) { + if (!class_exists($test->getName(), false)) { + $testSuites[] = $test; + continue; + } + $groups = $Test::getGroups($test->getName()); + if (in_array('time-sensitive', $groups, true)) { + ClockMock::register($test->getName()); + } + if (in_array('dns-sensitive', $groups, true)) { + DnsMock::register($test->getName()); + } + } + } + } + } elseif (2 === $this->state) { + $skipped = array(); + foreach ($suite->tests() as $test) { + if (!($test instanceof \PHPUnit_Framework_TestCase || $test instanceof TestCase) + || isset($this->wasSkipped[$suiteName]['*']) + || isset($this->wasSkipped[$suiteName][$test->getName()])) { + $skipped[] = $test; + } + } + $suite->setTests($skipped); + } + } + + public function addSkippedTest($test, \Exception $e, $time) + { + if (0 < $this->state) { + if ($test instanceof \PHPUnit_Framework_TestCase || $test instanceof TestCase) { + $class = get_class($test); + $method = $test->getName(); + } else { + $class = $test->getName(); + $method = '*'; + } + + $this->isSkipped[$class][$method] = 1; + } + } + + public function startTest($test) + { + if (-2 < $this->state && ($test instanceof \PHPUnit_Framework_TestCase || $test instanceof TestCase)) { + if (null !== $test->getTestResultObject()) { + $this->reportUselessTests = $test->getTestResultObject()->isStrictAboutTestsThatDoNotTestAnything(); + } + + if (class_exists('PHPUnit_Util_Blacklist', false)) { + $Test = 'PHPUnit_Util_Test'; + $AssertionFailedError = 'PHPUnit_Framework_AssertionFailedError'; + } else { + $Test = 'PHPUnit\Util\Test'; + $AssertionFailedError = 'PHPUnit\Framework\AssertionFailedError'; + } + $groups = $Test::getGroups(get_class($test), $test->getName(false)); + + if (in_array('time-sensitive', $groups, true)) { + ClockMock::register(get_class($test)); + ClockMock::withClockMock(true); + } + if (in_array('dns-sensitive', $groups, true)) { + DnsMock::register(get_class($test)); + } + + $annotations = $Test::parseTestMethodAnnotations(get_class($test), $test->getName(false)); + + if (isset($annotations['class']['expectedDeprecation'])) { + $test->getTestResultObject()->addError($test, new $AssertionFailedError('`@expectedDeprecation` annotations are not allowed at the class level.'), 0); + } + if (isset($annotations['method']['expectedDeprecation'])) { + if (!in_array('legacy', $groups, true)) { + $this->error = new $AssertionFailedError('Only tests with the `@group legacy` annotation can have `@expectedDeprecation`.'); + } + + $test->getTestResultObject()->beStrictAboutTestsThatDoNotTestAnything(false); + + $this->expectedDeprecations = $annotations['method']['expectedDeprecation']; + $this->previousErrorHandler = set_error_handler(array($this, 'handleError')); + } + } + } + + public function addWarning($test, $e, $time) + { + if ($test instanceof \PHPUnit_Framework_TestCase || $test instanceof TestCase) { + $this->testsWithWarnings[$test->getName()] = true; + } + } + + public function endTest($test, $time) + { + if (class_exists('PHPUnit_Util_Blacklist', false)) { + $Test = 'PHPUnit_Util_Test'; + $BaseTestRunner = 'PHPUnit_Runner_BaseTestRunner'; + $Warning = 'PHPUnit_Framework_Warning'; + } else { + $Test = 'PHPUnit\Util\Test'; + $BaseTestRunner = 'PHPUnit\Runner\BaseTestRunner'; + $Warning = 'PHPUnit\Framework\Warning'; + } + $className = get_class($test); + $classGroups = $Test::getGroups($className); + $groups = $Test::getGroups($className, $test->getName(false)); + + if (null !== $this->reportUselessTests) { + $test->getTestResultObject()->beStrictAboutTestsThatDoNotTestAnything($this->reportUselessTests); + $this->reportUselessTests = null; + } + + $errored = false; + + if (null !== $this->error) { + if ($BaseTestRunner::STATUS_PASSED === $test->getStatus()) { + $test->getTestResultObject()->addError($test, $this->error, 0); + $errored = true; + } + + $this->error = null; + } + + if ($this->expectedDeprecations) { + if (!in_array($test->getStatus(), array($BaseTestRunner::STATUS_SKIPPED, $BaseTestRunner::STATUS_INCOMPLETE), true)) { + $test->addToAssertionCount(count($this->expectedDeprecations)); + } + + restore_error_handler(); + + if (!$errored && !in_array($test->getStatus(), array($BaseTestRunner::STATUS_SKIPPED, $BaseTestRunner::STATUS_INCOMPLETE, $BaseTestRunner::STATUS_FAILURE, $BaseTestRunner::STATUS_ERROR), true)) { + try { + $prefix = "@expectedDeprecation:\n"; + $test->assertStringMatchesFormat($prefix.'%A '.implode("\n%A ", $this->expectedDeprecations)."\n%A", $prefix.' '.implode("\n ", $this->gatheredDeprecations)."\n"); + } catch (AssertionFailedError $e) { + $test->getTestResultObject()->addFailure($test, $e, $time); + } catch (\PHPUnit_Framework_AssertionFailedError $e) { + $test->getTestResultObject()->addFailure($test, $e, $time); + } + } + + $this->expectedDeprecations = $this->gatheredDeprecations = array(); + $this->previousErrorHandler = null; + } + if (-2 < $this->state && ($test instanceof \PHPUnit_Framework_TestCase || $test instanceof TestCase)) { + if (in_array('time-sensitive', $groups, true)) { + ClockMock::withClockMock(false); + } + if (in_array('dns-sensitive', $groups, true)) { + DnsMock::withMockedHosts(array()); + } + } + + if (($test instanceof \PHPUnit_Framework_TestCase || $test instanceof TestCase) && 0 === strpos($test->getName(), 'testLegacy') && !isset($this->testsWithWarnings[$test->getName()]) && !in_array('legacy', $groups, true)) { + $result = $test->getTestResultObject(); + + if (method_exists($result, 'addWarning')) { + $result->addWarning($test, new $Warning('Using the "testLegacy" prefix to mark tests as legacy is deprecated since version 3.3 and will be removed in 4.0. Use the "@group legacy" notation instead to add the test to the legacy group.'), $time); + } + } + + if (($test instanceof \PHPUnit_Framework_TestCase || $test instanceof TestCase) && strpos($className, '\Legacy') && !isset($this->testsWithWarnings[$test->getName()]) && !in_array('legacy', $classGroups, true)) { + $result = $test->getTestResultObject(); + + if (method_exists($result, 'addWarning')) { + $result->addWarning($test, new $Warning('Using the "Legacy" prefix to mark all tests of a class as legacy is deprecated since version 3.3 and will be removed in 4.0. Use the "@group legacy" notation instead to add the test to the legacy group.'), $time); + } + } + } + + public function handleError($type, $msg, $file, $line, $context) + { + if (E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) { + $h = $this->previousErrorHandler; + + return $h ? $h($type, $msg, $file, $line, $context) : false; + } + if (error_reporting()) { + $msg = 'Unsilenced deprecation: '.$msg; + } + $this->gatheredDeprecations[] = $msg; + } +} diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/TestRunner.php b/src/Symfony/Bridge/PhpUnit/Legacy/TestRunner.php new file mode 100644 index 0000000000000..4bbf2f1cb9724 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Legacy/TestRunner.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit\Legacy; + +/** + * {@inheritdoc} + * + * @internal + */ +class TestRunner extends \PHPUnit_TextUI_TestRunner +{ + /** + * {@inheritdoc} + */ + protected function handleConfiguration(array &$arguments) + { + $listener = new SymfonyTestsListener(); + + $result = parent::handleConfiguration($arguments); + + $arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : array(); + + $registeredLocally = false; + + foreach ($arguments['listeners'] as $registeredListener) { + if ($registeredListener instanceof SymfonyTestsListener) { + $registeredListener->globalListenerDisabled(); + $registeredLocally = true; + break; + } + } + + if (!$registeredLocally) { + $arguments['listeners'][] = $listener; + } + + return $result; + } +} diff --git a/src/Symfony/Bridge/PhpUnit/SymfonyTestsListener.php b/src/Symfony/Bridge/PhpUnit/SymfonyTestsListener.php index 0d873732636a9..f85dbb367ca9c 100644 --- a/src/Symfony/Bridge/PhpUnit/SymfonyTestsListener.php +++ b/src/Symfony/Bridge/PhpUnit/SymfonyTestsListener.php @@ -11,201 +11,60 @@ namespace Symfony\Bridge\PhpUnit; -use Doctrine\Common\Annotations\AnnotationRegistry; +use PHPUnit\Framework\BaseTestListener; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Framework\Warning; + +if (class_exists('PHPUnit_Framework_BaseTestListener')) { + class_alias('Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListener', 'Symfony\Bridge\PhpUnit\SymfonyTestsListener'); + + return; +} /** * Collects and replays skipped tests. * * @author Nicolas Grekas + * + * @final */ -class SymfonyTestsListener extends \PHPUnit_Framework_BaseTestListener +class SymfonyTestsListener extends BaseTestListener { - private static $globallyEnabled = false; - private $state = -1; - private $skippedFile = false; - private $wasSkipped = array(); - private $isSkipped = array(); - private $expectedDeprecations = array(); - private $gatheredDeprecations = array(); - private $previousErrorHandler; + private $trait; - /** - * @param array $mockedNamespaces List of namespaces, indexed by mocked features (time-sensitive or dns-sensitive) - */ public function __construct(array $mockedNamespaces = array()) { - $warn = false; - foreach ($mockedNamespaces as $type => $namespaces) { - if (!is_array($namespaces)) { - $namespaces = array($namespaces); - } - if (is_int($type)) { - // @deprecated BC with v2.8 to v3.0 - $type = 'time-sensitive'; - $warn = true; - } - if ('time-sensitive' === $type) { - foreach ($namespaces as $ns) { - ClockMock::register($ns.'\DummyClass'); - } - } - if ('dns-sensitive' === $type) { - foreach ($namespaces as $ns) { - DnsMock::register($ns.'\DummyClass'); - } - } - } - if (self::$globallyEnabled) { - $this->state = -2; - } else { - self::$globallyEnabled = true; - if ($warn) { - echo "Clock-mocked namespaces for SymfonyTestsListener need to be nested in a \"time-sensitive\" key. This will be enforced in Symfony 4.0.\n"; - } - } + $this->trait = new Legacy\SymfonyTestsListenerTrait($mockedNamespaces); } - public function __destruct() + public function globalListenerDisabled() { - if (0 < $this->state) { - file_put_contents($this->skippedFile, 'isSkipped, true).';'); - } + $this->trait->globalListenerDisabled(); } - public function startTestSuite(\PHPUnit_Framework_TestSuite $suite) + public function startTestSuite(TestSuite $suite) { - $suiteName = $suite->getName(); - - if (-1 === $this->state) { - echo "Testing $suiteName\n"; - $this->state = 0; - - if (!class_exists('Doctrine\Common\Annotations\AnnotationRegistry', false) && class_exists('Doctrine\Common\Annotations\AnnotationRegistry')) { - AnnotationRegistry::registerLoader('class_exists'); - } - - if ($this->skippedFile = getenv('SYMFONY_PHPUNIT_SKIPPED_TESTS')) { - $this->state = 1; - - if (file_exists($this->skippedFile)) { - $this->state = 2; - - if (!$this->wasSkipped = require $this->skippedFile) { - echo "All tests already ran successfully.\n"; - $suite->setTests(array()); - } - } - } - $testSuites = array($suite); - for ($i = 0; isset($testSuites[$i]); ++$i) { - foreach ($testSuites[$i]->tests() as $test) { - if ($test instanceof \PHPUnit_Framework_TestSuite) { - if (!class_exists($test->getName(), false)) { - $testSuites[] = $test; - continue; - } - $groups = \PHPUnit_Util_Test::getGroups($test->getName()); - if (in_array('time-sensitive', $groups, true)) { - ClockMock::register($test->getName()); - } - if (in_array('dns-sensitive', $groups, true)) { - DnsMock::register($test->getName()); - } - } - } - } - } elseif (2 === $this->state) { - $skipped = array(); - foreach ($suite->tests() as $test) { - if (!$test instanceof \PHPUnit_Framework_TestCase - || isset($this->wasSkipped[$suiteName]['*']) - || isset($this->wasSkipped[$suiteName][$test->getName()])) { - $skipped[] = $test; - } - } - $suite->setTests($skipped); - } + return $this->trait->startTestSuite($suite); } - public function addSkippedTest(\PHPUnit_Framework_Test $test, \Exception $e, $time) + public function addSkippedTest(Test $test, \Exception $e, $time) { - if (0 < $this->state) { - if ($test instanceof \PHPUnit_Framework_TestCase) { - $class = get_class($test); - $method = $test->getName(); - } else { - $class = $test->getName(); - $method = '*'; - } - - $this->isSkipped[$class][$method] = 1; - } + return $this->trait->addSkippedTest($test, $e, $time); } - public function startTest(\PHPUnit_Framework_Test $test) + public function startTest(Test $test) { - if (-2 < $this->state && $test instanceof \PHPUnit_Framework_TestCase) { - $groups = \PHPUnit_Util_Test::getGroups(get_class($test), $test->getName(false)); - - if (in_array('time-sensitive', $groups, true)) { - ClockMock::register(get_class($test)); - ClockMock::withClockMock(true); - } - if (in_array('dns-sensitive', $groups, true)) { - DnsMock::register(get_class($test)); - } - - $annotations = \PHPUnit_Util_Test::parseTestMethodAnnotations(get_class($test), $test->getName(false)); - - if (isset($annotations['class']['expectedDeprecation'])) { - $test->getTestResultObject()->addError($test, new \PHPUnit_Framework_AssertionFailedError('`@expectedDeprecation` annotations are not allowed at the class level.'), 0); - } - if (isset($annotations['method']['expectedDeprecation'])) { - if (!in_array('legacy', $groups, true)) { - $test->getTestResultObject()->addError($test, new \PHPUnit_Framework_AssertionFailedError('Only tests with the `@group legacy` annotation can have `@expectedDeprecation`.'), 0); - } - $this->expectedDeprecations = $annotations['method']['expectedDeprecation']; - $this->previousErrorHandler = set_error_handler(array($this, 'handleError')); - } - } + return $this->trait->startTest($test); } - public function endTest(\PHPUnit_Framework_Test $test, $time) + public function addWarning(Test $test, Warning $e, $time) { - if ($this->expectedDeprecations) { - restore_error_handler(); - try { - $prefix = "@expectedDeprecation:\n "; - $test->assertStringMatchesFormat($prefix.implode("\n ", $this->expectedDeprecations), $prefix.implode("\n ", $this->gatheredDeprecations)); - } catch (\PHPUnit_Framework_AssertionFailedError $e) { - $test->getTestResultObject()->addFailure($test, $e, $time); - } - - $this->expectedDeprecations = $this->gatheredDeprecations = array(); - $this->previousErrorHandler = null; - } - if (-2 < $this->state && $test instanceof \PHPUnit_Framework_TestCase) { - $groups = \PHPUnit_Util_Test::getGroups(get_class($test), $test->getName(false)); - - if (in_array('time-sensitive', $groups, true)) { - ClockMock::withClockMock(false); - } - if (in_array('dns-sensitive', $groups, true)) { - DnsMock::withMockedHosts(array()); - } - } + return $this->trait->addWarning($test, $e, $time); } - public function handleError($type, $msg, $file, $line, $context) + public function endTest(Test $test, $time) { - if (E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) { - $h = $this->previousErrorHandler; - - return $h ? $h($type, $msg, $file, $line, $context) : false; - } - if (error_reporting()) { - $msg = 'Unsilenced deprecation: '.$msg; - } - $this->gatheredDeprecations[] = $msg; + return $this->trait->endTest($test, $time); } } diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/default.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/default.phpt index fac5c53ae7486..cd733724870cd 100644 --- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/default.phpt +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/default.phpt @@ -18,6 +18,19 @@ require_once __DIR__.'/../../bootstrap.php'; @trigger_error('root deprecation', E_USER_DEPRECATED); +eval(<<<'EOPHP' +namespace PHPUnit\Util; + +class Test +{ + public static function getGroups() + { + return array(); + } +} +EOPHP +); + class PHPUnit_Util_Test { public static function getGroups() diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/acme/lib/deprecation_riddled.php b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/acme/lib/deprecation_riddled.php new file mode 100644 index 0000000000000..6f5123d4feb0c --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/acme/lib/deprecation_riddled.php @@ -0,0 +1,35 @@ +testLegacyFoo(); +$foo->testNonLegacyBar(); diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/autoload.php b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/autoload.php new file mode 100644 index 0000000000000..bf315f2eaa312 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/autoload.php @@ -0,0 +1,3 @@ +testLegacyFoo(); +$foo->testNonLegacyBar(); + +?> +--EXPECTF-- +Unsilenced deprecation notices (3) + +unsilenced foo deprecation: 2x + 2x in FooTestCase::testLegacyFoo + +unsilenced bar deprecation: 1x + 1x in FooTestCase::testNonLegacyBar + +Remaining deprecation notices (1) + +silenced bar deprecation: 1x + 1x in FooTestCase::testNonLegacyBar + +Legacy deprecation notices (1) + +Other deprecation notices (1) + +root deprecation: 1x + diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_vendor.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_vendor.phpt new file mode 100644 index 0000000000000..7bbda8775d6d5 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_vendor.phpt @@ -0,0 +1,27 @@ +--TEST-- +Test DeprecationErrorHandler in weak vendors mode on vendor file +--FILE-- +globalListenerDisabled(); + $registeredLocally = true; + break; + } + } + + if (!$registeredLocally) { + $arguments['listeners'][] = $listener; + } + + return $result; } } diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit index 9f846a3ecbaae..4683682a1bf6e 100755 --- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit +++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit @@ -16,7 +16,7 @@ error_reporting(-1); // PHPUnit 4.8 does not support PHP 7, while 5.1 requires PHP 5.6+ -$PHPUNIT_VERSION = PHP_VERSION_ID >= 50600 ? getenv('SYMFONY_PHPUNIT_VERSION') ?: '5.3' : '4.8'; +$PHPUNIT_VERSION = PHP_VERSION_ID >= 50600 ? getenv('SYMFONY_PHPUNIT_VERSION') ?: '5.7' : '4.8'; $oldPwd = getcwd(); $PHPUNIT_DIR = getenv('SYMFONY_PHPUNIT_DIR') ?: (__DIR__.'/.phpunit'); $PHP = defined('PHP_BINARY') ? PHP_BINARY : 'php'; @@ -37,12 +37,15 @@ if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit") || md5_file(__ if (file_exists("phpunit-$PHPUNIT_VERSION")) { passthru(sprintf('\\' === DIRECTORY_SEPARATOR ? '(del /S /F /Q %s & rmdir %1$s) >nul': 'rm -rf %s', "phpunit-$PHPUNIT_VERSION")); } - if (extension_loaded('openssl') && ini_get('allow_url_fopen')) { + if (extension_loaded('openssl') && ini_get('allow_url_fopen') && !isset($_SERVER['http_proxy']) && !isset($_SERVER['https_proxy'])) { stream_copy_to_stream(fopen("https://github.com/sebastianbergmann/phpunit/archive/$PHPUNIT_VERSION.zip", 'rb'), fopen("$PHPUNIT_VERSION.zip", 'wb')); } else { @unlink("$PHPUNIT_VERSION.zip"); passthru("wget https://github.com/sebastianbergmann/phpunit/archive/$PHPUNIT_VERSION.zip"); } + if (!class_exists('ZipArchive')) { + throw new \Exception('simple-phpunit requires the "zip" PHP extension to be installed and enabled in order to uncompress the downloaded PHPUnit packages.'); + } $zip = new ZipArchive(); $zip->open("$PHPUNIT_VERSION.zip"); $zip->extractTo(getcwd()); @@ -53,7 +56,10 @@ if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit") || md5_file(__ passthru("$COMPOSER require --no-update phpunit/phpunit-mock-objects \"~3.1.0\""); } passthru("$COMPOSER require --no-update symfony/phpunit-bridge \">=3.2@dev\""); - passthru("$COMPOSER install --no-dev --prefer-dist --no-progress --ansi", $exit); + $prevRoot = getenv('COMPOSER_ROOT_VERSION'); + putenv("COMPOSER_ROOT_VERSION=$PHPUNIT_VERSION"); + $exit = proc_close(proc_open("$COMPOSER install --no-dev --prefer-dist --no-progress --ansi", array(), $p, getcwd(), null, array('bypass_shell' => true))); + putenv('COMPOSER_ROOT_VERSION'.(false !== $prevRoot ? '='.$prevRoot : '')); if ($exit) { exit($exit); } @@ -180,27 +186,10 @@ if ($components) { } } } elseif (!isset($argv[1]) || 'install' !== $argv[1] || file_exists('install')) { - // Run regular phpunit in a subprocess - - $errFile = tempnam(sys_get_temp_dir(), 'phpunit.stderr.'); - if ($proc = proc_open(sprintf($cmd, '', ' 2> '.escapeshellarg($errFile)), array(1 => array('pipe', 'w')), $pipes)) { - stream_copy_to_stream($pipes[1], STDOUT); - fclose($pipes[1]); - $exit = proc_close($proc); - - readfile($errFile); - unlink($errFile); - } - - if (!file_exists($component = array_pop($argv))) { - $component = basename($oldPwd); - } - - if ($exit) { - echo "\033[41mKO\033[0m $component\n\n"; - } else { - echo "\033[32mOK\033[0m $component\n\n"; - } + array_splice($argv, 1, 0, array('--colors=always')); + $_SERVER['argv'] = $argv; + $_SERVER['argc'] = ++$argc; + include "$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit"; } exit($exit); diff --git a/src/Symfony/Bridge/PhpUnit/bootstrap.php b/src/Symfony/Bridge/PhpUnit/bootstrap.php index 5e2ed0ca85f82..f2ceffb1b51ae 100644 --- a/src/Symfony/Bridge/PhpUnit/bootstrap.php +++ b/src/Symfony/Bridge/PhpUnit/bootstrap.php @@ -13,7 +13,7 @@ use Symfony\Bridge\PhpUnit\DeprecationErrorHandler; // Detect if we're loaded by an actual run of phpunit -if (!defined('PHPUNIT_COMPOSER_INSTALL') && !class_exists('PHPUnit_TextUI_Command', false)) { +if (!defined('PHPUNIT_COMPOSER_INSTALL') && !class_exists('PHPUnit_TextUI_Command', false) && !class_exists('PHPUnit\TextUI\Command', false)) { return; } diff --git a/src/Symfony/Bridge/PhpUnit/composer.json b/src/Symfony/Bridge/PhpUnit/composer.json index 1cf3ef04318e5..847bb691d3ad8 100644 --- a/src/Symfony/Bridge/PhpUnit/composer.json +++ b/src/Symfony/Bridge/PhpUnit/composer.json @@ -21,7 +21,11 @@ "php": ">=5.3.3" }, "suggest": { - "symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader" + "symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader", + "ext-zip": "Zip support is required when using bin/simple-phpunit" + }, + "conflict": { + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" }, "autoload": { "files": [ "bootstrap.php" ], @@ -36,7 +40,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.3-dev" } } } diff --git a/src/Symfony/Bridge/PhpUnit/phpunit.xml.dist b/src/Symfony/Bridge/PhpUnit/phpunit.xml.dist index 9b64b02947c0e..816cfe4927ed3 100644 --- a/src/Symfony/Bridge/PhpUnit/phpunit.xml.dist +++ b/src/Symfony/Bridge/PhpUnit/phpunit.xml.dist @@ -5,6 +5,8 @@ backupGlobals="false" colors="true" bootstrap="vendor/autoload.php" + failOnRisky="true" + failOnWarning="true" > diff --git a/src/Symfony/Bridge/ProxyManager/LICENSE b/src/Symfony/Bridge/ProxyManager/LICENSE index 12a74531e40a4..17d16a13367dd 100644 --- a/src/Symfony/Bridge/ProxyManager/LICENSE +++ b/src/Symfony/Bridge/ProxyManager/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2016 Fabien Potencier +Copyright (c) 2004-2017 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.php index 3f3c577b846df..b634a69488a34 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.php @@ -13,6 +13,7 @@ require_once __DIR__.'/Fixtures/includes/foo.php'; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -22,7 +23,7 @@ * * @author Marco Pivetta */ -class ContainerBuilderTest extends \PHPUnit_Framework_TestCase +class ContainerBuilderTest extends TestCase { public function testCreateProxyServiceWithRuntimeInstantiator() { diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Dumper/PhpDumperTest.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Dumper/PhpDumperTest.php index 789799bce6e19..a191901753d24 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Dumper/PhpDumperTest.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Dumper/PhpDumperTest.php @@ -11,6 +11,7 @@ namespace Symfony\Bridge\ProxyManager\Tests\LazyProxy\Dumper; +use PHPUnit\Framework\TestCase; use ProxyManager\ProxyGenerator\LazyLoading\MethodGenerator\StaticProxyConstructor; use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -22,7 +23,7 @@ * * @author Marco Pivetta */ -class PhpDumperTest extends \PHPUnit_Framework_TestCase +class PhpDumperTest extends TestCase { public function testDumpContainerWithProxyService() { diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service.php index fe98c150f71a2..77e04571e089c 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service.php @@ -122,9 +122,6 @@ public function __unset($name) unset($this->valueHolder5157dd96e88c0->$name); } - /** - * - */ public function __clone() { $this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, '__clone', array()); @@ -132,9 +129,6 @@ public function __clone() $this->valueHolder5157dd96e88c0 = clone $this->valueHolder5157dd96e88c0; } - /** - * - */ public function __sleep() { $this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, '__sleep', array()); @@ -142,9 +136,6 @@ public function __sleep() return array('valueHolder5157dd96e88c0'); } - /** - * - */ public function __wakeup() { } diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service_with_hints.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service_with_hints.php index 12409e8e637bf..6787b9ff8ed45 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service_with_hints.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service_with_hints.php @@ -122,9 +122,6 @@ public function __unset($name) unset($this->valueHolder5157dd96e88c0->$name); } - /** - * - */ public function __clone() { $this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, '__clone', array()); @@ -132,9 +129,6 @@ public function __clone() $this->valueHolder5157dd96e88c0 = clone $this->valueHolder5157dd96e88c0; } - /** - * - */ public function __sleep() { $this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, '__sleep', array()); @@ -142,9 +136,6 @@ public function __sleep() return array('valueHolder5157dd96e88c0'); } - /** - * - */ public function __wakeup() { } @@ -168,7 +159,7 @@ public function getProxyInitializer() /** * {@inheritdoc} */ - public function initializeProxy() : bool + public function initializeProxy(): bool { return $this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, 'initializeProxy', array()); } @@ -176,7 +167,7 @@ public function initializeProxy() : bool /** * {@inheritdoc} */ - public function isProxyInitialized() : bool + public function isProxyInitialized(): bool { return null !== $this->valueHolder5157dd96e88c0; } diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Instantiator/RuntimeInstantiatorTest.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Instantiator/RuntimeInstantiatorTest.php index 8b2402b045f28..e58b7d6356161 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Instantiator/RuntimeInstantiatorTest.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Instantiator/RuntimeInstantiatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Bridge\ProxyManager\Tests\LazyProxy\Instantiator; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator; use Symfony\Component\DependencyInjection\Definition; @@ -19,7 +20,7 @@ * * @author Marco Pivetta */ -class RuntimeInstantiatorTest extends \PHPUnit_Framework_TestCase +class RuntimeInstantiatorTest extends TestCase { /** * @var RuntimeInstantiator @@ -37,7 +38,7 @@ protected function setUp() public function testInstantiateProxy() { $instance = new \stdClass(); - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); $definition = new Definition('stdClass'); $instantiator = function () use ($instance) { return $instance; diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php index 8b6555cfe2556..64082c8f7f3f1 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php @@ -11,6 +11,7 @@ namespace Symfony\Bridge\ProxyManager\Tests\LazyProxy\PhpDumper; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper; use Symfony\Component\DependencyInjection\Definition; @@ -19,7 +20,7 @@ * * @author Marco Pivetta */ -class ProxyDumperTest extends \PHPUnit_Framework_TestCase +class ProxyDumperTest extends TestCase { /** * @var ProxyDumper diff --git a/src/Symfony/Bridge/ProxyManager/composer.json b/src/Symfony/Bridge/ProxyManager/composer.json index f6d0d22aed972..42751d000e59d 100644 --- a/src/Symfony/Bridge/ProxyManager/composer.json +++ b/src/Symfony/Bridge/ProxyManager/composer.json @@ -32,7 +32,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.3-dev" } } } diff --git a/src/Symfony/Bridge/ProxyManager/phpunit.xml.dist b/src/Symfony/Bridge/ProxyManager/phpunit.xml.dist index 60980be9e531e..f280e037fec08 100644 --- a/src/Symfony/Bridge/ProxyManager/phpunit.xml.dist +++ b/src/Symfony/Bridge/ProxyManager/phpunit.xml.dist @@ -5,6 +5,8 @@ backupGlobals="false" colors="true" bootstrap="vendor/autoload.php" + failOnRisky="true" + failOnWarning="true" > diff --git a/src/Symfony/Bridge/Twig/AppVariable.php b/src/Symfony/Bridge/Twig/AppVariable.php index 02fe5e5b68526..e8e69d0ee3d68 100644 --- a/src/Symfony/Bridge/Twig/AppVariable.php +++ b/src/Symfony/Bridge/Twig/AppVariable.php @@ -145,4 +145,40 @@ public function getDebug() return $this->debug; } + + /** + * Returns some or all the existing flash messages: + * * getFlashes() returns all the flash messages + * * getFlashes('notice') returns a simple array with flash messages of that type + * * getFlashes(array('notice', 'error')) returns a nested array of type => messages. + * + * @return array + */ + public function getFlashes($types = null) + { + // needed to avoid starting the session automatically when looking for flash messages + try { + $session = $this->getSession(); + if (null === $session || !$session->isStarted()) { + return array(); + } + } catch (\RuntimeException $e) { + return array(); + } + + if (null === $types || '' === $types || array() === $types) { + return $session->getFlashBag()->all(); + } + + if (is_string($types)) { + return $session->getFlashBag()->get($types); + } + + $result = array(); + foreach ($types as $type) { + $result[$type] = $session->getFlashBag()->get($type); + } + + return $result; + } } diff --git a/src/Symfony/Bridge/Twig/CHANGELOG.md b/src/Symfony/Bridge/Twig/CHANGELOG.md index 685e1c9b083f0..9c635c9508f29 100644 --- a/src/Symfony/Bridge/Twig/CHANGELOG.md +++ b/src/Symfony/Bridge/Twig/CHANGELOG.md @@ -1,12 +1,48 @@ CHANGELOG ========= +3.3.0 +----- + + * added a `workflow_has_marked_place` function + * added a `workflow_marked_places` function + 3.2.0 ----- * added `AppVariable::getToken()` - * Deprecated the possibility to inject the Form Twig Renderer into the form - extension. Inject it on TwigRendererEngine instead. + * Deprecated the possibility to inject the Form `TwigRenderer` into the `FormExtension`. + * [BC BREAK] Registering the `FormExtension` without configuring a runtime loader for the `TwigRenderer` + doesn't work anymore. + + Before: + + ```php + use Symfony\Bridge\Twig\Extension\FormExtension; + use Symfony\Bridge\Twig\Form\TwigRenderer; + use Symfony\Bridge\Twig\Form\TwigRendererEngine; + + // ... + $rendererEngine = new TwigRendererEngine(array('form_div_layout.html.twig')); + $rendererEngine->setEnvironment($twig); + $twig->addExtension(new FormExtension(new TwigRenderer($rendererEngine, $csrfTokenManager))); + ``` + + After: + + ```php + // ... + $rendererEngine = new TwigRendererEngine(array('form_div_layout.html.twig'), $twig); + // require Twig 1.30+ + $twig->addRuntimeLoader(new \Twig_FactoryRuntimeLoader(array( + TwigRenderer::class => function () use ($rendererEngine, $csrfTokenManager) { + return new TwigRenderer($rendererEngine, $csrfTokenManager); + }, + ))); + $twig->addExtension(new FormExtension()); + ``` + * Deprecated the `TwigRendererEngineInterface` interface. + * added WorkflowExtension (provides `workflow_can` and `workflow_transitions`) 2.7.0 ----- diff --git a/src/Symfony/Bridge/Twig/Command/DebugCommand.php b/src/Symfony/Bridge/Twig/Command/DebugCommand.php index 44e23902e03de..4748444cd181f 100644 --- a/src/Symfony/Bridge/Twig/Command/DebugCommand.php +++ b/src/Symfony/Bridge/Twig/Command/DebugCommand.php @@ -87,9 +87,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $twig = $this->getTwigEnvironment(); if (null === $twig) { - $io->error('The Twig environment needs to be set.'); - - return 1; + throw new \RuntimeException('The Twig environment needs to be set.'); } $types = array('functions', 'filters', 'tests', 'globals'); @@ -140,7 +138,7 @@ private function getMetadata($type, $entity) } if ($type === 'functions' || $type === 'filters') { $cb = $entity->getCallable(); - if (is_null($cb)) { + if (null === $cb) { return; } if (is_array($cb)) { diff --git a/src/Symfony/Bridge/Twig/Command/LintCommand.php b/src/Symfony/Bridge/Twig/Command/LintCommand.php index ebbfde12c2058..b252ee274cd57 100644 --- a/src/Symfony/Bridge/Twig/Command/LintCommand.php +++ b/src/Symfony/Bridge/Twig/Command/LintCommand.php @@ -88,9 +88,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $io = new SymfonyStyle($input, $output); if (null === $twig = $this->getTwigEnvironment()) { - $io->error('The Twig environment needs to be set.'); - - return 1; + throw new \RuntimeException('The Twig environment needs to be set.'); } $filenames = $input->getArgument('filename'); diff --git a/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php b/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php index 90b21d0495ff7..f075709654c95 100644 --- a/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php +++ b/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php @@ -93,7 +93,11 @@ public function getHtmlCallGraph() public function getProfile() { if (null === $this->profile) { - $this->profile = unserialize($this->data['profile']); + if (PHP_VERSION_ID >= 70000) { + $this->profile = unserialize($this->data['profile'], array('allowed_classes' => array('Twig_Profiler_Profile'))); + } else { + $this->profile = unserialize($this->data['profile']); + } } return $this->profile; diff --git a/src/Symfony/Bridge/Twig/Extension/FormExtension.php b/src/Symfony/Bridge/Twig/Extension/FormExtension.php index 016e6a9586c97..81a8683c35b97 100644 --- a/src/Symfony/Bridge/Twig/Extension/FormExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/FormExtension.php @@ -13,6 +13,7 @@ use Symfony\Bridge\Twig\TokenParser\FormThemeTokenParser; use Symfony\Bridge\Twig\Form\TwigRendererInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Form\ChoiceList\View\ChoiceView; /** @@ -23,12 +24,17 @@ */ class FormExtension extends \Twig_Extension implements \Twig_Extension_InitRuntimeInterface { + /** + * @deprecated since version 3.2, to be removed in 4.0 alongside with magic methods below + */ private $renderer; - public function __construct(TwigRendererInterface $renderer = null) + public function __construct($renderer = null) { - if (null !== $this->renderer) { + if ($renderer instanceof TwigRendererInterface) { @trigger_error(sprintf('Passing a Twig Form Renderer to the "%s" constructor is deprecated since version 3.2 and won\'t be possible in 4.0. Pass the Twig_Environment to the TwigRendererEngine constructor instead.', static::class), E_USER_DEPRECATED); + } elseif (null !== $renderer && !(is_array($renderer) && isset($renderer[0], $renderer[1]) && $renderer[0] instanceof ContainerInterface)) { + throw new \InvalidArgumentException(sprintf('Passing any arguments the constructor of %s is reserved for internal use.', __CLASS__)); } $this->renderer = $renderer; } @@ -40,8 +46,10 @@ public function __construct(TwigRendererInterface $renderer = null) */ public function initRuntime(\Twig_Environment $environment) { - if (null !== $this->renderer) { + if ($this->renderer instanceof TwigRendererInterface) { $this->renderer->setEnvironment($environment); + } elseif (null !== $this->renderer) { + $this->renderer[2] = $environment; } } @@ -94,6 +102,62 @@ public function getTests() ); } + /** + * @internal + */ + public function __get($name) + { + if ('renderer' === $name) { + @trigger_error(sprintf('Using the "%s::$renderer" property is deprecated since version 3.2 as it will be removed in 4.0.', __CLASS__), E_USER_DEPRECATED); + + if (is_array($this->renderer)) { + $renderer = $this->renderer[0]->get($this->renderer[1]); + if (isset($this->renderer[2])) { + $renderer->setEnvironment($this->renderer[2]); + } + $this->renderer = $renderer; + } + } + + return $this->$name; + } + + /** + * @internal + */ + public function __set($name, $value) + { + if ('renderer' === $name) { + @trigger_error(sprintf('Using the "%s::$renderer" property is deprecated since version 3.2 as it will be removed in 4.0.', __CLASS__), E_USER_DEPRECATED); + } + + $this->$name = $value; + } + + /** + * @internal + */ + public function __isset($name) + { + if ('renderer' === $name) { + @trigger_error(sprintf('Using the "%s::$renderer" property is deprecated since version 3.2 as it will be removed in 4.0.', __CLASS__), E_USER_DEPRECATED); + } + + return isset($this->$name); + } + + /** + * @internal + */ + public function __unset($name) + { + if ('renderer' === $name) { + @trigger_error(sprintf('Using the "%s::$renderer" property is deprecated since version 3.2 as it will be removed in 4.0.', __CLASS__), E_USER_DEPRECATED); + } + + unset($this->$name); + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Bridge/Twig/Extension/RoutingExtension.php b/src/Symfony/Bridge/Twig/Extension/RoutingExtension.php index 7469183e75de1..81cef949c7408 100644 --- a/src/Symfony/Bridge/Twig/Extension/RoutingExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/RoutingExtension.php @@ -40,11 +40,25 @@ public function getFunctions() ); } + /** + * @param string $name + * @param array $parameters + * @param bool $relative + * + * @return string + */ public function getPath($name, $parameters = array(), $relative = false) { return $this->generator->generate($name, $parameters, $relative ? UrlGeneratorInterface::RELATIVE_PATH : UrlGeneratorInterface::ABSOLUTE_PATH); } + /** + * @param string $name + * @param array $parameters + * @param bool $schemeRelative + * + * @return string + */ public function getUrl($name, $parameters = array(), $schemeRelative = false) { return $this->generator->generate($name, $parameters, $schemeRelative ? UrlGeneratorInterface::NETWORK_PATH : UrlGeneratorInterface::ABSOLUTE_URL); diff --git a/src/Symfony/Bridge/Twig/Extension/WebLinkExtension.php b/src/Symfony/Bridge/Twig/Extension/WebLinkExtension.php new file mode 100644 index 0000000000000..14e1fc2736bae --- /dev/null +++ b/src/Symfony/Bridge/Twig/Extension/WebLinkExtension.php @@ -0,0 +1,137 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Extension; + +use Fig\Link\GenericLinkProvider; +use Fig\Link\Link; +use Symfony\Component\HttpFoundation\RequestStack; + +/** + * Twig extension for the Symfony WebLink component. + * + * @author Kévin Dunglas + */ +class WebLinkExtension extends \Twig_Extension +{ + private $requestStack; + + public function __construct(RequestStack $requestStack) + { + $this->requestStack = $requestStack; + } + + /** + * {@inheritdoc} + */ + public function getFunctions() + { + return array( + new \Twig_SimpleFunction('link', array($this, 'link')), + new \Twig_SimpleFunction('preload', array($this, 'preload')), + new \Twig_SimpleFunction('dns_prefetch', array($this, 'dnsPrefetch')), + new \Twig_SimpleFunction('preconnect', array($this, 'preconnect')), + new \Twig_SimpleFunction('prefetch', array($this, 'prefetch')), + new \Twig_SimpleFunction('prerender', array($this, 'prerender')), + ); + } + + /** + * Adds a "Link" HTTP header. + * + * @param string $uri The relation URI + * @param string $rel The relation type (e.g. "preload", "prefetch", "prerender" or "dns-prefetch") + * @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)") + * + * @return string The relation URI + */ + public function link($uri, $rel, array $attributes = array()) + { + if (!$request = $this->requestStack->getMasterRequest()) { + return $uri; + } + + $link = new Link($rel, $uri); + foreach ($attributes as $key => $value) { + $link = $link->withAttribute($key, $value); + } + + $linkProvider = $request->attributes->get('_links', new GenericLinkProvider()); + $request->attributes->set('_links', $linkProvider->withLink($link)); + + return $uri; + } + + /** + * Preloads a resource. + * + * @param string $uri A public path + * @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('crossorigin' => 'use-credentials')") + * + * @return string The path of the asset + */ + public function preload($uri, array $attributes = array()) + { + return $this->link($uri, 'preload', $attributes); + } + + /** + * Resolves a resource origin as early as possible. + * + * @param string $uri A public path + * @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)") + * + * @return string The path of the asset + */ + public function dnsPrefetch($uri, array $attributes = array()) + { + return $this->link($uri, 'dns-prefetch', $attributes); + } + + /** + * Initiates a early connection to a resource (DNS resolution, TCP handshake, TLS negotiation). + * + * @param string $uri A public path + * @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)") + * + * @return string The path of the asset + */ + public function preconnect($uri, array $attributes = array()) + { + return $this->link($uri, 'preconnect', $attributes); + } + + /** + * Indicates to the client that it should prefetch this resource. + * + * @param string $uri A public path + * @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)") + * + * @return string The path of the asset + */ + public function prefetch($uri, array $attributes = array()) + { + return $this->link($uri, 'prefetch', $attributes); + } + + /** + * Indicates to the client that it should prerender this resource . + * + * @param string $uri A public path + * @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)") + * + * @return string The path of the asset + */ + public function prerender($uri, array $attributes = array()) + { + return $this->link($uri, 'prerender', $attributes); + } +} diff --git a/src/Symfony/Bridge/Twig/Extension/WorkflowExtension.php b/src/Symfony/Bridge/Twig/Extension/WorkflowExtension.php index c2c5a55af954f..0e63283ab1905 100644 --- a/src/Symfony/Bridge/Twig/Extension/WorkflowExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/WorkflowExtension.php @@ -32,17 +32,70 @@ public function getFunctions() return array( new \Twig_SimpleFunction('workflow_can', array($this, 'canTransition')), new \Twig_SimpleFunction('workflow_transitions', array($this, 'getEnabledTransitions')), + new \Twig_SimpleFunction('workflow_has_marked_place', array($this, 'hasMarkedPlace')), + new \Twig_SimpleFunction('workflow_marked_places', array($this, 'getMarkedPlaces')), ); } - public function canTransition($object, $transition, $name = null) + /** + * Returns true if the transition is enabled. + * + * @param object $subject A subject + * @param string $transitionName A transition + * @param string $name A workflow name + * + * @return bool true if the transition is enabled + */ + public function canTransition($subject, $transitionName, $name = null) { - return $this->workflowRegistry->get($object, $name)->can($object, $transition); + return $this->workflowRegistry->get($subject, $name)->can($subject, $transitionName); } - public function getEnabledTransitions($object, $name = null) + /** + * Returns all enabled transitions. + * + * @param object $subject A subject + * @param string $name A workflow name + * + * @return Transition[] All enabled transitions + */ + public function getEnabledTransitions($subject, $name = null) { - return $this->workflowRegistry->get($object, $name)->getEnabledTransitions($object); + return $this->workflowRegistry->get($subject, $name)->getEnabledTransitions($subject); + } + + /** + * Returns true if the place is marked. + * + * @param object $subject A subject + * @param string $placeName A place name + * @param string $name A workflow name + * + * @return bool true if the transition is enabled + */ + public function hasMarkedPlace($subject, $placeName, $name = null) + { + return $this->workflowRegistry->get($subject, $name)->getMarking($subject)->has($placeName); + } + + /** + * Returns marked places. + * + * @param object $subject A subject + * @param string $placesNameOnly If true, returns only places name. If false returns the raw representation + * @param string $name A workflow name + * + * @return string[]|int[] + */ + public function getMarkedPlaces($subject, $placesNameOnly = true, $name = null) + { + $places = $this->workflowRegistry->get($subject, $name)->getMarking($subject)->getPlaces(); + + if ($placesNameOnly) { + return array_keys($places); + } + + return $places; } public function getName() diff --git a/src/Symfony/Bridge/Twig/Form/TwigRendererEngine.php b/src/Symfony/Bridge/Twig/Form/TwigRendererEngine.php index fe99929df0b3d..406726ccdcba0 100644 --- a/src/Symfony/Bridge/Twig/Form/TwigRendererEngine.php +++ b/src/Symfony/Bridge/Twig/Form/TwigRendererEngine.php @@ -41,9 +41,15 @@ public function __construct(array $defaultThemes = array(), \Twig_Environment $e /** * {@inheritdoc} + * + * @deprecated since version 3.3, to be removed in 4.0 */ public function setEnvironment(\Twig_Environment $environment) { + if ($this->environment) { + @trigger_error(sprintf('The "%s()" method is deprecated since version 3.3 and will be removed in 4.0. Pass the Twig Environment as second argument of the constructor instead.', __METHOD__), E_USER_DEPRECATED); + } + $this->environment = $environment; } diff --git a/src/Symfony/Bridge/Twig/Form/TwigRendererEngineInterface.php b/src/Symfony/Bridge/Twig/Form/TwigRendererEngineInterface.php index c5968a1fa7683..2fd0cbda79a99 100644 --- a/src/Symfony/Bridge/Twig/Form/TwigRendererEngineInterface.php +++ b/src/Symfony/Bridge/Twig/Form/TwigRendererEngineInterface.php @@ -16,7 +16,7 @@ /** * @author Bernhard Schussek * - * @deprecated Deprecated since version 3.2, to be removed in 4.0. + * @deprecated since version 3.2, to be removed in 4.0. */ interface TwigRendererEngineInterface extends FormRendererEngineInterface { diff --git a/src/Symfony/Bridge/Twig/Form/TwigRendererInterface.php b/src/Symfony/Bridge/Twig/Form/TwigRendererInterface.php index 2b2172b8cce5a..85133248f3850 100644 --- a/src/Symfony/Bridge/Twig/Form/TwigRendererInterface.php +++ b/src/Symfony/Bridge/Twig/Form/TwigRendererInterface.php @@ -16,7 +16,7 @@ /** * @author Bernhard Schussek * - * @deprecated Deprecated since version 3.2, to be removed in 4.0. + * @deprecated since version 3.2, to be removed in 4.0. */ interface TwigRendererInterface extends FormRendererInterface { diff --git a/src/Symfony/Bridge/Twig/LICENSE b/src/Symfony/Bridge/Twig/LICENSE index 12a74531e40a4..17d16a13367dd 100644 --- a/src/Symfony/Bridge/Twig/LICENSE +++ b/src/Symfony/Bridge/Twig/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2016 Fabien Potencier +Copyright (c) 2004-2017 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php b/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php index f9333bf683d1a..1284cf52a20b7 100644 --- a/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php +++ b/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php @@ -42,7 +42,7 @@ public function __construct(Scope $parent = null) /** * Opens a new child scope. * - * @return Scope + * @return self */ public function enter() { @@ -52,7 +52,7 @@ public function enter() /** * Closes current scope and returns parent one. * - * @return Scope|null + * @return self|null */ public function leave() { @@ -67,7 +67,7 @@ public function leave() * @param string $key * @param mixed $value * - * @return Scope Current scope + * @return $this * * @throws \LogicException */ diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig index 5fb3a18f1d532..f2fe8bf3d2588 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig @@ -94,13 +94,32 @@ {%- set attr = attr|merge({class: (attr.class|default('') ~ ' form-inline')|trim}) -%}
{{- form_errors(form) -}} - {%- if with_years %}{{ form_widget(form.years) }}{% endif -%} - {%- if with_months %}{{ form_widget(form.months) }}{% endif -%} - {%- if with_weeks %}{{ form_widget(form.weeks) }}{% endif -%} - {%- if with_days %}{{ form_widget(form.days) }}{% endif -%} - {%- if with_hours %}{{ form_widget(form.hours) }}{% endif -%} - {%- if with_minutes %}{{ form_widget(form.minutes) }}{% endif -%} - {%- if with_seconds %}{{ form_widget(form.seconds) }}{% endif -%} +
+ + + + {%- if with_years %}{% endif -%} + {%- if with_months %}{% endif -%} + {%- if with_weeks %}{% endif -%} + {%- if with_days %}{% endif -%} + {%- if with_hours %}{% endif -%} + {%- if with_minutes %}{% endif -%} + {%- if with_seconds %}{% endif -%} + + + + + {%- if with_years %}{% endif -%} + {%- if with_months %}{% endif -%} + {%- if with_weeks %}{% endif -%} + {%- if with_days %}{% endif -%} + {%- if with_hours %}{% endif -%} + {%- if with_minutes %}{% endif -%} + {%- if with_seconds %}{% endif -%} + + +
{{ form_label(form.years) }}{{ form_label(form.months) }}{{ form_label(form.weeks) }}{{ form_label(form.days) }}{{ form_label(form.hours) }}{{ form_label(form.minutes) }}{{ form_label(form.seconds) }}
{{ form_widget(form.years) }}{{ form_widget(form.months) }}{{ form_widget(form.weeks) }}{{ form_widget(form.days) }}{{ form_widget(form.hours) }}{{ form_widget(form.minutes) }}{{ form_widget(form.seconds) }}
+
{%- if with_invert %}{{ form_widget(form.invert) }}{% endif -%}
{%- endif -%} 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 e1d09153dfe60..82d52706d9a1a 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 @@ -79,7 +79,7 @@ {{- block('choice_widget_options') -}} {%- else -%} - + {%- endif -%} {% endfor %} {%- endblock choice_widget_options -%} @@ -136,13 +136,30 @@ {%- else -%}
{{- form_errors(form) -}} - {%- if with_years %}{{ form_widget(form.years) }}{% endif -%} - {%- if with_months %}{{ form_widget(form.months) }}{% endif -%} - {%- if with_weeks %}{{ form_widget(form.weeks) }}{% endif -%} - {%- if with_days %}{{ form_widget(form.days) }}{% endif -%} - {%- if with_hours %}{{ form_widget(form.hours) }}{% endif -%} - {%- if with_minutes %}{{ form_widget(form.minutes) }}{% endif -%} - {%- if with_seconds %}{{ form_widget(form.seconds) }}{% endif -%} + + + + {%- if with_years %}{% endif -%} + {%- if with_months %}{% endif -%} + {%- if with_weeks %}{% endif -%} + {%- if with_days %}{% endif -%} + {%- if with_hours %}{% endif -%} + {%- if with_minutes %}{% endif -%} + {%- if with_seconds %}{% endif -%} + + + + + {%- if with_years %}{% endif -%} + {%- if with_months %}{% endif -%} + {%- if with_weeks %}{% endif -%} + {%- if with_days %}{% endif -%} + {%- if with_hours %}{% endif -%} + {%- if with_minutes %}{% endif -%} + {%- if with_seconds %}{% endif -%} + + +
{{ form_label(form.years) }}{{ form_label(form.months) }}{{ form_label(form.weeks) }}{{ form_label(form.days) }}{{ form_label(form.hours) }}{{ form_label(form.minutes) }}{{ form_label(form.seconds) }}
{{ form_widget(form.years) }}{{ form_widget(form.months) }}{{ form_widget(form.weeks) }}{{ form_widget(form.days) }}{{ form_widget(form.hours) }}{{ form_widget(form.minutes) }}{{ form_widget(form.seconds) }}
{%- if with_invert %}{{ form_widget(form.invert) }}{% endif -%}
{%- endif -%} @@ -242,7 +259,7 @@ {% set label = name|humanize %} {%- endif -%} {%- endif -%} - {{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }} + {{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }} {%- endif -%} {%- endblock form_label -%} @@ -334,44 +351,17 @@ id="{{ id }}" name="{{ full_name }}" {%- if disabled %} disabled="disabled"{% endif -%} {%- if required %} required="required"{% endif -%} - {%- for attrname, attrvalue in attr -%} - {{- " " -}} - {%- if attrname in ['placeholder', 'title'] -%} - {{- attrname }}="{{ translation_domain is same as(false) ? attrvalue : attrvalue|trans({}, translation_domain) }}" - {%- elseif attrvalue is same as(true) -%} - {{- attrname }}="{{ attrname }}" - {%- elseif attrvalue is not same as(false) -%} - {{- attrname }}="{{ attrvalue }}" - {%- endif -%} - {%- endfor -%} + {{ block('attributes') }} {%- endblock widget_attributes -%} {%- block widget_container_attributes -%} {%- if id is not empty %}id="{{ id }}"{% endif -%} - {%- for attrname, attrvalue in attr -%} - {{- " " -}} - {%- if attrname in ['placeholder', 'title'] -%} - {{- attrname }}="{{ translation_domain is same as(false) ? attrvalue : attrvalue|trans({}, translation_domain) }}" - {%- elseif attrvalue is same as(true) -%} - {{- attrname }}="{{ attrname }}" - {%- elseif attrvalue is not same as(false) -%} - {{- attrname }}="{{ attrvalue }}" - {%- endif -%} - {%- endfor -%} + {{ block('attributes') }} {%- endblock widget_container_attributes -%} {%- block button_attributes -%} id="{{ id }}" name="{{ full_name }}"{% if disabled %} disabled="disabled"{% endif -%} - {%- for attrname, attrvalue in attr -%} - {{- " " -}} - {%- if attrname in ['placeholder', 'title'] -%} - {{- attrname }}="{{ translation_domain is same as(false) ? attrvalue : attrvalue|trans({}, translation_domain) }}" - {%- elseif attrvalue is same as(true) -%} - {{- attrname }}="{{ attrname }}" - {%- elseif attrvalue is not same as(false) -%} - {{- attrname }}="{{ attrvalue }}" - {%- endif -%} - {%- endfor -%} + {{ block('attributes') }} {%- endblock button_attributes -%} {% block attributes -%} diff --git a/src/Symfony/Bridge/Twig/Tests/AppVariableTest.php b/src/Symfony/Bridge/Twig/Tests/AppVariableTest.php index c54ad28ea875a..2a97c269031c8 100644 --- a/src/Symfony/Bridge/Twig/Tests/AppVariableTest.php +++ b/src/Symfony/Bridge/Twig/Tests/AppVariableTest.php @@ -2,11 +2,13 @@ namespace Symfony\Bridge\Twig\Tests; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\AppVariable; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; use Symfony\Component\HttpFoundation\Session\Session; -class AppVariableTest extends \PHPUnit_Framework_TestCase +class AppVariableTest extends TestCase { /** * @var AppVariable @@ -45,7 +47,7 @@ public function testEnvironment() public function testGetSession() { - $request = $this->getMock('Symfony\Component\HttpFoundation\Request'); + $request = $this->getMockBuilder('Symfony\Component\HttpFoundation\Request')->getMock(); $request->method('getSession')->willReturn($session = new Session()); $this->setRequestStack($request); @@ -69,10 +71,10 @@ public function testGetRequest() public function testGetToken() { - $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'); + $tokenStorage = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface')->getMock(); $this->appVariable->setTokenStorage($tokenStorage); - $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock(); $tokenStorage->method('getToken')->willReturn($token); $this->assertEquals($token, $this->appVariable->getToken()); @@ -80,7 +82,7 @@ public function testGetToken() public function testGetUser() { - $this->setTokenStorage($user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface')); + $this->setTokenStorage($user = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock()); $this->assertEquals($user, $this->appVariable->getUser()); } @@ -94,7 +96,7 @@ public function testGetUserWithUsernameAsTokenUser() public function testGetTokenWithNoToken() { - $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'); + $tokenStorage = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface')->getMock(); $this->appVariable->setTokenStorage($tokenStorage); $this->assertNull($this->appVariable->getToken()); @@ -102,7 +104,7 @@ public function testGetTokenWithNoToken() public function testGetUserWithNoToken() { - $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'); + $tokenStorage = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface')->getMock(); $this->appVariable->setTokenStorage($tokenStorage); $this->assertNull($this->appVariable->getUser()); @@ -156,9 +158,79 @@ public function testGetSessionWithRequestStackNotSet() $this->appVariable->getSession(); } + public function testGetFlashesWithNoRequest() + { + $this->setRequestStack(null); + + $this->assertEquals(array(), $this->appVariable->getFlashes()); + } + + public function testGetFlashesWithNoSessionStarted() + { + $request = $this->getMockBuilder('Symfony\Component\HttpFoundation\Request')->getMock(); + $request->method('getSession')->willReturn(new Session()); + + $this->setRequestStack($request); + + $this->assertEquals(array(), $this->appVariable->getFlashes()); + } + + public function testGetFlashes() + { + $flashMessages = $this->setFlashMessages(); + $this->assertEquals($flashMessages, $this->appVariable->getFlashes(null)); + + $flashMessages = $this->setFlashMessages(); + $this->assertEquals($flashMessages, $this->appVariable->getFlashes('')); + + $flashMessages = $this->setFlashMessages(); + $this->assertEquals($flashMessages, $this->appVariable->getFlashes(array())); + + $flashMessages = $this->setFlashMessages(); + $this->assertEquals(array(), $this->appVariable->getFlashes('this-does-not-exist')); + + $flashMessages = $this->setFlashMessages(); + $this->assertEquals( + array('this-does-not-exist' => array()), + $this->appVariable->getFlashes(array('this-does-not-exist')) + ); + + $flashMessages = $this->setFlashMessages(); + $this->assertEquals($flashMessages['notice'], $this->appVariable->getFlashes('notice')); + + $flashMessages = $this->setFlashMessages(); + $this->assertEquals( + array('notice' => $flashMessages['notice']), + $this->appVariable->getFlashes(array('notice')) + ); + + $flashMessages = $this->setFlashMessages(); + $this->assertEquals( + array('notice' => $flashMessages['notice'], 'this-does-not-exist' => array()), + $this->appVariable->getFlashes(array('notice', 'this-does-not-exist')) + ); + + $flashMessages = $this->setFlashMessages(); + $this->assertEquals( + array('notice' => $flashMessages['notice'], 'error' => $flashMessages['error']), + $this->appVariable->getFlashes(array('notice', 'error')) + ); + + $this->assertEquals( + array('warning' => $flashMessages['warning']), + $this->appVariable->getFlashes(array('warning')), + 'After getting some flash types (e.g. "notice" and "error"), the rest of flash messages must remain (e.g. "warning").' + ); + + $this->assertEquals( + array('this-does-not-exist' => array()), + $this->appVariable->getFlashes(array('this-does-not-exist')) + ); + } + protected function setRequestStack($request) { - $requestStackMock = $this->getMock('Symfony\Component\HttpFoundation\RequestStack'); + $requestStackMock = $this->getMockBuilder('Symfony\Component\HttpFoundation\RequestStack')->getMock(); $requestStackMock->method('getCurrentRequest')->willReturn($request); $this->appVariable->setRequestStack($requestStackMock); @@ -166,12 +238,33 @@ protected function setRequestStack($request) protected function setTokenStorage($user) { - $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'); + $tokenStorage = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface')->getMock(); $this->appVariable->setTokenStorage($tokenStorage); - $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock(); $tokenStorage->method('getToken')->willReturn($token); $token->method('getUser')->willReturn($user); } + + private function setFlashMessages() + { + $flashMessages = array( + 'notice' => array('Notice #1 message'), + 'warning' => array('Warning #1 message'), + 'error' => array('Error #1 message', 'Error #2 message'), + ); + $flashBag = new FlashBag(); + $flashBag->initialize($flashMessages); + + $session = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\Session')->getMock(); + $session->method('isStarted')->willReturn(true); + $session->method('getFlashBag')->willReturn($flashBag); + + $request = $this->getMockBuilder('Symfony\Component\HttpFoundation\Request')->getMock(); + $request->method('getSession')->willReturn($session); + $this->setRequestStack($request); + + return $flashMessages; + } } diff --git a/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php b/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php index 68b20dbbc98f0..1b63cdd1b8daf 100644 --- a/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php @@ -11,12 +11,13 @@ namespace Symfony\Bridge\Twig\Tests\Command; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\Command\LintCommand; use Symfony\Component\Console\Application; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Tester\CommandTester; -class LintCommandTest extends \PHPUnit_Framework_TestCase +class LintCommandTest extends TestCase { private $files; diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/CodeExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/CodeExtensionTest.php index e1883a526d767..336991c6ca9f2 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/CodeExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/CodeExtensionTest.php @@ -11,13 +11,12 @@ namespace Symfony\Bridge\Twig\Tests\Extension; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\Extension\CodeExtension; use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; -class CodeExtensionTest extends \PHPUnit_Framework_TestCase +class CodeExtensionTest extends TestCase { - protected $helper; - public function testFormatFile() { $expected = sprintf('%s at line 25', substr(__FILE__, 5), __FILE__); diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php index 92803be90e662..df95e4b450b45 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php @@ -11,12 +11,13 @@ namespace Symfony\Bridge\Twig\Tests\Extension; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\Extension\DumpExtension; use Symfony\Component\VarDumper\Dumper\HtmlDumper; use Symfony\Component\VarDumper\VarDumper; use Symfony\Component\VarDumper\Cloner\VarCloner; -class DumpExtensionTest extends \PHPUnit_Framework_TestCase +class DumpExtensionTest extends TestCase { /** * @dataProvider getDumpTags @@ -33,7 +34,7 @@ public function testDumpTag($template, $debug, $expectedOutput, $expectedDumped) $dumped = null; $exception = null; - $prevDumper = VarDumper::setHandler(function ($var) use (&$dumped) {$dumped = $var;}); + $prevDumper = VarDumper::setHandler(function ($var) use (&$dumped) { $dumped = $var; }); try { $this->assertEquals($expectedOutput, $twig->render('template')); @@ -64,7 +65,7 @@ public function getDumpTags() public function testDump($context, $args, $expectedOutput, $debug = true) { $extension = new DumpExtension(new VarCloner()); - $twig = new \Twig_Environment($this->getMock('Twig_LoaderInterface'), array( + $twig = new \Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array( 'debug' => $debug, 'cache' => false, 'optimizations' => 0, @@ -120,7 +121,7 @@ public function testCustomDumper() '' ); $extension = new DumpExtension(new VarCloner(), $dumper); - $twig = new \Twig_Environment($this->getMock('Twig_LoaderInterface'), array( + $twig = new \Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array( 'debug' => true, 'cache' => false, 'optimizations' => 0, diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/ExpressionExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/ExpressionExtensionTest.php index 6d457e395b07e..d689bad20b118 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/ExpressionExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/ExpressionExtensionTest.php @@ -11,12 +11,11 @@ namespace Symfony\Bridge\Twig\Tests\Extension; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\Extension\ExpressionExtension; -class ExpressionExtensionTest extends \PHPUnit_Framework_TestCase +class ExpressionExtensionTest extends TestCase { - protected $helper; - public function testExpressionCreation() { $template = "{{ expression('1 == 1') }}"; diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php index 7ebaa4c2bad51..614b59f5823ec 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php @@ -47,7 +47,7 @@ protected function setUp() 'bootstrap_3_horizontal_layout.html.twig', 'custom_widgets.html.twig', ), $environment); - $this->renderer = new TwigRenderer($rendererEngine, $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface')); + $this->renderer = new TwigRenderer($rendererEngine, $this->getMockBuilder('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface')->getMock()); $this->registerTwigRuntimeLoader($environment, $this->renderer); } diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php index f7919a44f964c..aceda8153d434 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php @@ -43,7 +43,7 @@ protected function setUp() 'bootstrap_3_layout.html.twig', 'custom_widgets.html.twig', ), $environment); - $this->renderer = new TwigRenderer($rendererEngine, $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface')); + $this->renderer = new TwigRenderer($rendererEngine, $this->getMockBuilder('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface')->getMock()); $this->registerTwigRuntimeLoader($environment, $this->renderer); } diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php index f9eb355db31b8..99672997441be 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php @@ -47,7 +47,7 @@ protected function setUp() 'form_div_layout.html.twig', 'custom_widgets.html.twig', ), $environment); - $this->renderer = new TwigRenderer($rendererEngine, $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface')); + $this->renderer = new TwigRenderer($rendererEngine, $this->getMockBuilder('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface')->getMock()); $this->registerTwigRuntimeLoader($environment, $this->renderer); } @@ -89,7 +89,10 @@ public function testThemeBlockInheritanceUsingDynamicExtend() ; $this->renderer->setTheme($view, array('page_dynamic_extends.html.twig')); - $this->renderer->searchAndRenderBlock($view, 'row'); + $this->assertMatchesXpath( + $this->renderer->searchAndRenderBlock($view, 'row'), + '/div/label[text()="child"]' + ); } public function isSelectedChoiceProvider() diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php index a2726af4390c4..8d4396a54c4dd 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php @@ -44,7 +44,7 @@ protected function setUp() 'form_table_layout.html.twig', 'custom_widgets.html.twig', ), $environment); - $this->renderer = new TwigRenderer($rendererEngine, $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface')); + $this->renderer = new TwigRenderer($rendererEngine, $this->getMockBuilder('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface')->getMock()); $this->registerTwigRuntimeLoader($environment, $this->renderer); } diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/HttpFoundationExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/HttpFoundationExtensionTest.php index 339d43d7c6bd1..8f0c66ad78bb4 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/HttpFoundationExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/HttpFoundationExtensionTest.php @@ -11,12 +11,13 @@ namespace Symfony\Bridge\Twig\Tests\Extension; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\Extension\HttpFoundationExtension; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\RequestContext; -class HttpFoundationExtensionTest extends \PHPUnit_Framework_TestCase +class HttpFoundationExtensionTest extends TestCase { /** * @dataProvider getGenerateAbsoluteUrlData() diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php index a583f661d3514..8909b3e4e43b4 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php @@ -11,13 +11,14 @@ namespace Symfony\Bridge\Twig\Tests\Extension; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\Extension\HttpKernelExtension; use Symfony\Bridge\Twig\Extension\HttpKernelRuntime; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Fragment\FragmentHandler; -class HttpKernelExtensionTest extends \PHPUnit_Framework_TestCase +class HttpKernelExtensionTest extends TestCase { /** * @expectedException \Twig_Error_Runtime @@ -46,13 +47,19 @@ public function testUnknownFragmentRenderer() ; $renderer = new FragmentHandler($context); - $this->setExpectedException('InvalidArgumentException', 'The "inline" renderer does not exist.'); + if (method_exists($this, 'expectException')) { + $this->expectException('InvalidArgumentException'); + $this->expectExceptionMessage('The "inline" renderer does not exist.'); + } else { + $this->setExpectedException('InvalidArgumentException', 'The "inline" renderer does not exist.'); + } + $renderer->render('/foo'); } protected function getFragmentHandler($return) { - $strategy = $this->getMock('Symfony\\Component\\HttpKernel\\Fragment\\FragmentRendererInterface'); + $strategy = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\Fragment\\FragmentRendererInterface')->getMock(); $strategy->expects($this->once())->method('getName')->will($this->returnValue('inline')); $strategy->expects($this->once())->method('render')->will($return); @@ -63,9 +70,7 @@ protected function getFragmentHandler($return) $context->expects($this->any())->method('getCurrentRequest')->will($this->returnValue(Request::create('/'))); - $renderer = new FragmentHandler($context, array($strategy), false); - - return $renderer; + return new FragmentHandler($context, array($strategy), false); } protected function renderTemplate(FragmentHandler $renderer, $template = '{{ render("foo") }}') @@ -74,7 +79,7 @@ protected function renderTemplate(FragmentHandler $renderer, $template = '{{ ren $twig = new \Twig_Environment($loader, array('debug' => true, 'cache' => false)); $twig->addExtension(new HttpKernelExtension()); - $loader = $this->getMock('Twig_RuntimeLoaderInterface'); + $loader = $this->getMockBuilder('Twig_RuntimeLoaderInterface')->getMock(); $loader->expects($this->any())->method('load')->will($this->returnValueMap(array( array('Symfony\Bridge\Twig\Extension\HttpKernelRuntime', new HttpKernelRuntime($renderer)), ))); diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.php index 9733cd7b8ace6..5fa4e9cd36b1c 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.php @@ -11,17 +11,18 @@ namespace Symfony\Bridge\Twig\Tests\Extension; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\Extension\RoutingExtension; -class RoutingExtensionTest extends \PHPUnit_Framework_TestCase +class RoutingExtensionTest extends TestCase { /** * @dataProvider getEscapingTemplates */ public function testEscaping($template, $mustBeEscaped) { - $twig = new \Twig_Environment($this->getMock('Twig_LoaderInterface'), array('debug' => true, 'cache' => false, 'autoescape' => 'html', 'optimizations' => 0)); - $twig->addExtension(new RoutingExtension($this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface'))); + $twig = new \Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('debug' => true, 'cache' => false, 'autoescape' => 'html', 'optimizations' => 0)); + $twig->addExtension(new RoutingExtension($this->getMockBuilder('Symfony\Component\Routing\Generator\UrlGeneratorInterface')->getMock())); $nodes = $twig->parse($twig->tokenize(new \Twig_Source($template, ''))); diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/RuntimeLoaderProvider.php b/src/Symfony/Bridge/Twig/Tests/Extension/RuntimeLoaderProvider.php index 12ae07f8d48b0..a6c397ffd4d90 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/RuntimeLoaderProvider.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/RuntimeLoaderProvider.php @@ -17,7 +17,7 @@ trait RuntimeLoaderProvider { protected function registerTwigRuntimeLoader(\Twig_Environment $environment, TwigRenderer $renderer) { - $loader = $this->getMock('Twig_RuntimeLoaderInterface'); + $loader = $this->getMockBuilder('Twig_RuntimeLoaderInterface')->getMock(); $loader->expects($this->any())->method('load')->will($this->returnValueMap(array( array('Symfony\Bridge\Twig\Form\TwigRenderer', $renderer), ))); diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/StopwatchExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/StopwatchExtensionTest.php index daca220e493e0..c50252eaafcca 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/StopwatchExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/StopwatchExtensionTest.php @@ -11,9 +11,10 @@ namespace Symfony\Bridge\Twig\Tests\Extension; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\Extension\StopwatchExtension; -class StopwatchExtensionTest extends \PHPUnit_Framework_TestCase +class StopwatchExtensionTest extends TestCase { /** * @expectedException \Twig_Error_Syntax @@ -53,7 +54,7 @@ public function getTimingTemplates() protected function getStopwatch($events = array()) { $events = is_array($events) ? $events : array($events); - $stopwatch = $this->getMock('Symfony\Component\Stopwatch\Stopwatch'); + $stopwatch = $this->getMockBuilder('Symfony\Component\Stopwatch\Stopwatch')->getMock(); $i = -1; foreach ($events as $eventName) { diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php index e96bd4f9a3bef..446697d3dd8b2 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php @@ -11,12 +11,13 @@ namespace Symfony\Bridge\Twig\Tests\Extension; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\Extension\TranslationExtension; use Symfony\Component\Translation\Translator; use Symfony\Component\Translation\MessageSelector; use Symfony\Component\Translation\Loader\ArrayLoader; -class TranslationExtensionTest extends \PHPUnit_Framework_TestCase +class TranslationExtensionTest extends TestCase { public function testEscaping() { diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/WebLinkExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/WebLinkExtensionTest.php new file mode 100644 index 0000000000000..3424b58875fbc --- /dev/null +++ b/src/Symfony/Bridge/Twig/Tests/Extension/WebLinkExtensionTest.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Extension; + +use Fig\Link\Link; +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\Twig\Extension\WebLinkExtension; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; + +/** + * @author Kévin Dunglas + */ +class WebLinkExtensionTest extends TestCase +{ + /** + * @var Request + */ + private $request; + + /** + * @var WebLinkExtension + */ + private $extension; + + protected function setUp() + { + $this->request = new Request(); + + $requestStack = new RequestStack(); + $requestStack->push($this->request); + + $this->extension = new WebLinkExtension($requestStack); + } + + public function testLink() + { + $this->assertEquals('/foo.css', $this->extension->link('/foo.css', 'preload', array('as' => 'style', 'nopush' => true))); + + $link = (new Link('preload', '/foo.css'))->withAttribute('as', 'style')->withAttribute('nopush', true); + $this->assertEquals(array($link), array_values($this->request->attributes->get('_links')->getLinks())); + } + + public function testPreload() + { + $this->assertEquals('/foo.css', $this->extension->preload('/foo.css', array('as' => 'style', 'crossorigin' => true))); + + $link = (new Link('preload', '/foo.css'))->withAttribute('as', 'style')->withAttribute('crossorigin', true); + $this->assertEquals(array($link), array_values($this->request->attributes->get('_links')->getLinks())); + } + + public function testDnsPrefetch() + { + $this->assertEquals('/foo.css', $this->extension->dnsPrefetch('/foo.css', array('as' => 'style', 'crossorigin' => true))); + + $link = (new Link('dns-prefetch', '/foo.css'))->withAttribute('as', 'style')->withAttribute('crossorigin', true); + $this->assertEquals(array($link), array_values($this->request->attributes->get('_links')->getLinks())); + } + + public function testPreconnect() + { + $this->assertEquals('/foo.css', $this->extension->preconnect('/foo.css', array('as' => 'style', 'crossorigin' => true))); + + $link = (new Link('preconnect', '/foo.css'))->withAttribute('as', 'style')->withAttribute('crossorigin', true); + $this->assertEquals(array($link), array_values($this->request->attributes->get('_links')->getLinks())); + } + + public function testPrefetch() + { + $this->assertEquals('/foo.css', $this->extension->prefetch('/foo.css', array('as' => 'style', 'crossorigin' => true))); + + $link = (new Link('prefetch', '/foo.css'))->withAttribute('as', 'style')->withAttribute('crossorigin', true); + $this->assertEquals(array($link), array_values($this->request->attributes->get('_links')->getLinks())); + } + + public function testPrerender() + { + $this->assertEquals('/foo.css', $this->extension->prerender('/foo.css', array('as' => 'style', 'crossorigin' => true))); + + $link = (new Link('prerender', '/foo.css'))->withAttribute('as', 'style')->withAttribute('crossorigin', true); + $this->assertEquals(array($link), array_values($this->request->attributes->get('_links')->getLinks())); + } +} diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/WorkflowExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/WorkflowExtensionTest.php new file mode 100644 index 0000000000000..e134434c9b4e7 --- /dev/null +++ b/src/Symfony/Bridge/Twig/Tests/Extension/WorkflowExtensionTest.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Extension; + +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\Twig\Extension\WorkflowExtension; +use Symfony\Component\Workflow\Definition; +use Symfony\Component\Workflow\Marking; +use Symfony\Component\Workflow\Registry; +use Symfony\Component\Workflow\SupportStrategy\ClassInstanceSupportStrategy; +use Symfony\Component\Workflow\Transition; +use Symfony\Component\Workflow\Workflow; + +class WorkflowExtensionTest extends TestCase +{ + private $extension; + + protected function setUp() + { + if (!class_exists(Workflow::class)) { + $this->markTestSkipped('The Workflow component is needed to run tests for this extension.'); + } + + $places = array('ordered', 'waiting_for_payment', 'processed'); + $transitions = array( + new Transition('t1', 'ordered', 'waiting_for_payment'), + new Transition('t2', 'waiting_for_payment', 'processed'), + ); + $definition = new Definition($places, $transitions); + $workflow = new Workflow($definition); + + $registry = new Registry(); + $registry->add($workflow, new ClassInstanceSupportStrategy(\stdClass::class)); + + $this->extension = new WorkflowExtension($registry); + } + + public function testCanTransition() + { + $subject = new \stdClass(); + $subject->marking = array(); + + $this->assertTrue($this->extension->canTransition($subject, 't1')); + $this->assertFalse($this->extension->canTransition($subject, 't2')); + } + + public function testGetEnabledTransitions() + { + $subject = new \stdClass(); + $subject->marking = array(); + + $transitions = $this->extension->getEnabledTransitions($subject); + + $this->assertCount(1, $transitions); + $this->assertInstanceOf(Transition::class, $transitions[0]); + $this->assertSame('t1', $transitions[0]->getName()); + } + + public function testHasMarkedPlace() + { + $subject = new \stdClass(); + $subject->marking = array(); + $subject->marking = array('ordered' => 1, 'waiting_for_payment' => 1); + + $this->assertTrue($this->extension->hasMarkedPlace($subject, 'ordered')); + $this->assertTrue($this->extension->hasMarkedPlace($subject, 'waiting_for_payment')); + $this->assertFalse($this->extension->hasMarkedPlace($subject, 'processed')); + } + + public function testGetMarkedPlaces() + { + $subject = new \stdClass(); + $subject->marking = array(); + $subject->marking = array('ordered' => 1, 'waiting_for_payment' => 1); + + $this->assertSame(array('ordered', 'waiting_for_payment'), $this->extension->getMarkedPlaces($subject)); + $this->assertSame($subject->marking, $this->extension->getMarkedPlaces($subject, false)); + } +} diff --git a/src/Symfony/Bridge/Twig/Tests/Node/DumpNodeTest.php b/src/Symfony/Bridge/Twig/Tests/Node/DumpNodeTest.php index 5d669cde7ac94..5e589c2db11db 100644 --- a/src/Symfony/Bridge/Twig/Tests/Node/DumpNodeTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Node/DumpNodeTest.php @@ -11,15 +11,16 @@ namespace Symfony\Bridge\Twig\Tests\Node; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\Node\DumpNode; -class DumpNodeTest extends \PHPUnit_Framework_TestCase +class DumpNodeTest extends TestCase { public function testNoVar() { $node = new DumpNode('bar', null, 7); - $env = new \Twig_Environment($this->getMock('Twig_LoaderInterface')); + $env = new \Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); $compiler = new \Twig_Compiler($env); $expected = <<<'EOTXT' @@ -43,7 +44,7 @@ public function testIndented() { $node = new DumpNode('bar', null, 7); - $env = new \Twig_Environment($this->getMock('Twig_LoaderInterface')); + $env = new \Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); $compiler = new \Twig_Compiler($env); $expected = <<<'EOTXT' @@ -70,7 +71,7 @@ public function testOneVar() )); $node = new DumpNode('bar', $vars, 7); - $env = new \Twig_Environment($this->getMock('Twig_LoaderInterface')); + $env = new \Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); $compiler = new \Twig_Compiler($env); $expected = <<<'EOTXT' @@ -98,7 +99,7 @@ public function testMultiVars() )); $node = new DumpNode('bar', $vars, 7); - $env = new \Twig_Environment($this->getMock('Twig_LoaderInterface')); + $env = new \Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); $compiler = new \Twig_Compiler($env); $expected = <<<'EOTXT' diff --git a/src/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php b/src/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php index dff1d5f5ae090..9d3576e0b4430 100644 --- a/src/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php @@ -11,9 +11,10 @@ namespace Symfony\Bridge\Twig\Tests\Node; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\Node\FormThemeNode; -class FormThemeTest extends \PHPUnit_Framework_TestCase +class FormThemeTest extends TestCase { public function testConstructor() { @@ -41,7 +42,7 @@ public function testCompile() $node = new FormThemeNode($form, $resources, 0); - $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMock('Twig_LoaderInterface'))); + $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); $this->assertEquals( sprintf( diff --git a/src/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php b/src/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php index 31ab934f37ec8..b292ef63f8555 100644 --- a/src/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php @@ -11,9 +11,10 @@ namespace Symfony\Bridge\Twig\Tests\Node; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode; -class SearchAndRenderBlockNodeTest extends \PHPUnit_Framework_TestCase +class SearchAndRenderBlockNodeTest extends TestCase { public function testCompileWidget() { @@ -23,7 +24,7 @@ public function testCompileWidget() $node = new SearchAndRenderBlockNode('form_widget', $arguments, 0); - $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMock('Twig_LoaderInterface'))); + $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); $this->assertEquals( sprintf( @@ -46,7 +47,7 @@ public function testCompileWidgetWithVariables() $node = new SearchAndRenderBlockNode('form_widget', $arguments, 0); - $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMock('Twig_LoaderInterface'))); + $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); $this->assertEquals( sprintf( @@ -66,7 +67,7 @@ public function testCompileLabelWithLabel() $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); - $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMock('Twig_LoaderInterface'))); + $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); $this->assertEquals( sprintf( @@ -86,7 +87,7 @@ public function testCompileLabelWithNullLabel() $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); - $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMock('Twig_LoaderInterface'))); + $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); // "label" => null must not be included in the output! // Otherwise the default label is overwritten with null. @@ -108,7 +109,7 @@ public function testCompileLabelWithEmptyStringLabel() $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); - $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMock('Twig_LoaderInterface'))); + $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); // "label" => null must not be included in the output! // Otherwise the default label is overwritten with null. @@ -129,7 +130,7 @@ public function testCompileLabelWithDefaultLabel() $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); - $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMock('Twig_LoaderInterface'))); + $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); $this->assertEquals( sprintf( @@ -153,7 +154,7 @@ public function testCompileLabelWithAttributes() $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); - $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMock('Twig_LoaderInterface'))); + $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); // "label" => null must not be included in the output! // Otherwise the default label is overwritten with null. @@ -182,7 +183,7 @@ public function testCompileLabelWithLabelAndAttributes() $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); - $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMock('Twig_LoaderInterface'))); + $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); $this->assertEquals( sprintf( @@ -210,7 +211,7 @@ public function testCompileLabelWithLabelThatEvaluatesToNull() $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); - $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMock('Twig_LoaderInterface'))); + $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); // "label" => null must not be included in the output! // Otherwise the default label is overwritten with null. @@ -247,7 +248,7 @@ public function testCompileLabelWithLabelThatEvaluatesToNullAndAttributes() $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); - $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMock('Twig_LoaderInterface'))); + $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); // "label" => null must not be included in the output! // Otherwise the default label is overwritten with null. diff --git a/src/Symfony/Bridge/Twig/Tests/Node/TransNodeTest.php b/src/Symfony/Bridge/Twig/Tests/Node/TransNodeTest.php index 4c00cdab50ade..5862006f3826a 100644 --- a/src/Symfony/Bridge/Twig/Tests/Node/TransNodeTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Node/TransNodeTest.php @@ -11,12 +11,13 @@ namespace Symfony\Bridge\Twig\Tests\Node; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\Node\TransNode; /** * @author Asmir Mustafic */ -class TransNodeTest extends \PHPUnit_Framework_TestCase +class TransNodeTest extends TestCase { public function testCompileStrict() { @@ -24,7 +25,7 @@ public function testCompileStrict() $vars = new \Twig_Node_Expression_Name('foo', 0); $node = new TransNode($body, null, null, $vars); - $env = new \Twig_Environment($this->getMock('Twig_LoaderInterface'), array('strict_variables' => true)); + $env = new \Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('strict_variables' => true)); $compiler = new \Twig_Compiler($env); $this->assertEquals( @@ -49,7 +50,7 @@ protected function getVariableGetterWithoutStrictCheck($name) protected function getVariableGetterWithStrictCheck($name) { if (\Twig_Environment::MAJOR_VERSION >= 2) { - return sprintf('(isset($context["%s"]) || array_key_exists("%s", $context) ? $context["%s"] : $this->notFound("%s", 0))', $name, $name, $name, $name); + return sprintf('(isset($context["%s"]) || array_key_exists("%s", $context) ? $context["%s"] : (function () { throw new Twig_Error_Runtime(\'Variable "%s" does not exist.\', 0, $this->getSourceContext()); })())', $name, $name, $name, $name); } if (PHP_VERSION_ID >= 70000) { diff --git a/src/Symfony/Bridge/Twig/Tests/NodeVisitor/ScopeTest.php b/src/Symfony/Bridge/Twig/Tests/NodeVisitor/ScopeTest.php index aa9d204e5606f..fad0e1f829763 100644 --- a/src/Symfony/Bridge/Twig/Tests/NodeVisitor/ScopeTest.php +++ b/src/Symfony/Bridge/Twig/Tests/NodeVisitor/ScopeTest.php @@ -11,9 +11,10 @@ namespace Symfony\Bridge\Twig\Tests\NodeVisitor; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\NodeVisitor\Scope; -class ScopeTest extends \PHPUnit_Framework_TestCase +class ScopeTest extends TestCase { public function testScopeInitiation() { diff --git a/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationDefaultDomainNodeVisitorTest.php b/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationDefaultDomainNodeVisitorTest.php index f9cf08bc28017..da9f43a6c4e0e 100644 --- a/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationDefaultDomainNodeVisitorTest.php +++ b/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationDefaultDomainNodeVisitorTest.php @@ -11,10 +11,11 @@ namespace Symfony\Bridge\Twig\Tests\NodeVisitor; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\NodeVisitor\TranslationDefaultDomainNodeVisitor; use Symfony\Bridge\Twig\NodeVisitor\TranslationNodeVisitor; -class TranslationDefaultDomainNodeVisitorTest extends \PHPUnit_Framework_TestCase +class TranslationDefaultDomainNodeVisitorTest extends TestCase { private static $message = 'message'; private static $domain = 'domain'; @@ -22,7 +23,7 @@ class TranslationDefaultDomainNodeVisitorTest extends \PHPUnit_Framework_TestCas /** @dataProvider getDefaultDomainAssignmentTestData */ public function testDefaultDomainAssignment(\Twig_Node $node) { - $env = new \Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false, 'optimizations' => 0)); + $env = new \Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false, 'optimizations' => 0)); $visitor = new TranslationDefaultDomainNodeVisitor(); // visit trans_default_domain tag @@ -48,7 +49,7 @@ public function testDefaultDomainAssignment(\Twig_Node $node) /** @dataProvider getDefaultDomainAssignmentTestData */ public function testNewModuleWithoutDefaultDomainTag(\Twig_Node $node) { - $env = new \Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false, 'optimizations' => 0)); + $env = new \Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false, 'optimizations' => 0)); $visitor = new TranslationDefaultDomainNodeVisitor(); // visit trans_default_domain tag diff --git a/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.php b/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.php index 16736031e087d..d12fff532aaa1 100644 --- a/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.php +++ b/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.php @@ -11,14 +11,15 @@ namespace Symfony\Bridge\Twig\Tests\NodeVisitor; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\NodeVisitor\TranslationNodeVisitor; -class TranslationNodeVisitorTest extends \PHPUnit_Framework_TestCase +class TranslationNodeVisitorTest extends TestCase { /** @dataProvider getMessagesExtractionTestData */ public function testMessagesExtraction(\Twig_Node $node, array $expectedMessages) { - $env = new \Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false, 'optimizations' => 0)); + $env = new \Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false, 'optimizations' => 0)); $visitor = new TranslationNodeVisitor(); $visitor->enable(); $visitor->enterNode($node, $env); diff --git a/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php b/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php index 6b6a92abf1434..8931be061f9d2 100644 --- a/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php +++ b/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php @@ -11,17 +11,18 @@ namespace Symfony\Bridge\Twig\Tests\TokenParser; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\TokenParser\FormThemeTokenParser; use Symfony\Bridge\Twig\Node\FormThemeNode; -class FormThemeTokenParserTest extends \PHPUnit_Framework_TestCase +class FormThemeTokenParserTest extends TestCase { /** * @dataProvider getTestsForFormTheme */ public function testCompile($source, $expected) { - $env = new \Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false, 'optimizations' => 0)); + $env = new \Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false, 'optimizations' => 0)); $env->addTokenParser(new FormThemeTokenParser()); $stream = $env->tokenize(new \Twig_Source($source, '')); $parser = new \Twig_Parser($env); diff --git a/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php b/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php index 5e7c4ce5216d0..0b1fb28b0e10c 100644 --- a/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php @@ -11,25 +11,26 @@ namespace Symfony\Bridge\Twig\Tests\Translation; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\Extension\TranslationExtension; use Symfony\Bridge\Twig\Translation\TwigExtractor; use Symfony\Component\Translation\MessageCatalogue; -class TwigExtractorTest extends \PHPUnit_Framework_TestCase +class TwigExtractorTest extends TestCase { /** * @dataProvider getExtractData */ public function testExtract($template, $messages) { - $loader = $this->getMock('Twig_LoaderInterface'); + $loader = $this->getMockBuilder('Twig_LoaderInterface')->getMock(); $twig = new \Twig_Environment($loader, array( 'strict_variables' => true, 'debug' => true, 'cache' => false, 'autoescape' => false, )); - $twig->addExtension(new TranslationExtension($this->getMock('Symfony\Component\Translation\TranslatorInterface'))); + $twig->addExtension(new TranslationExtension($this->getMockBuilder('Symfony\Component\Translation\TranslatorInterface')->getMock())); $extractor = new TwigExtractor($twig); $extractor->setPrefix('prefix'); @@ -72,17 +73,28 @@ public function getExtractData() } /** - * @expectedException \Twig_Error - * @expectedExceptionMessageRegExp /Unclosed "block" in ".*extractor(\/|\\)syntax_error\.twig" at line 1/ + * @expectedException \Twig_Error * @dataProvider resourcesWithSyntaxErrorsProvider */ public function testExtractSyntaxError($resources) { - $twig = new \Twig_Environment($this->getMock('Twig_LoaderInterface')); - $twig->addExtension(new TranslationExtension($this->getMock('Symfony\Component\Translation\TranslatorInterface'))); + $twig = new \Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); + $twig->addExtension(new TranslationExtension($this->getMockBuilder('Symfony\Component\Translation\TranslatorInterface')->getMock())); $extractor = new TwigExtractor($twig); - $extractor->extract($resources, new MessageCatalogue('en')); + + try { + $extractor->extract($resources, new MessageCatalogue('en')); + } catch (\Twig_Error $e) { + if (method_exists($e, 'getSourceContext')) { + $this->assertSame(dirname(__DIR__).strtr('/Fixtures/extractor/syntax_error.twig', '/', DIRECTORY_SEPARATOR), $e->getFile()); + $this->assertSame(1, $e->getLine()); + $this->assertSame('Unclosed "block".', $e->getMessage()); + } else { + $this->expectExceptionMessageRegExp('/Unclosed "block" in ".*extractor(\\/|\\\\)syntax_error\\.twig" at line 1/'); + } + throw $e; + } } /** @@ -109,7 +121,7 @@ public function testExtractWithFiles($resource) 'cache' => false, 'autoescape' => false, )); - $twig->addExtension(new TranslationExtension($this->getMock('Symfony\Component\Translation\TranslatorInterface'))); + $twig->addExtension(new TranslationExtension($this->getMockBuilder('Symfony\Component\Translation\TranslatorInterface')->getMock())); $extractor = new TwigExtractor($twig); $catalogue = new MessageCatalogue('en'); diff --git a/src/Symfony/Bridge/Twig/Tests/TwigEngineTest.php b/src/Symfony/Bridge/Twig/Tests/TwigEngineTest.php index e7047c354d080..e2082df3dd75b 100644 --- a/src/Symfony/Bridge/Twig/Tests/TwigEngineTest.php +++ b/src/Symfony/Bridge/Twig/Tests/TwigEngineTest.php @@ -11,10 +11,11 @@ namespace Symfony\Bridge\Twig\Tests; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\TwigEngine; use Symfony\Component\Templating\TemplateReference; -class TwigEngineTest extends \PHPUnit_Framework_TestCase +class TwigEngineTest extends TestCase { public function testExistsWithTemplateInstances() { @@ -71,7 +72,7 @@ protected function getTwig() 'index' => 'foo', 'error' => '{{ foo }', ))); - $parser = $this->getMock('Symfony\Component\Templating\TemplateNameParserInterface'); + $parser = $this->getMockBuilder('Symfony\Component\Templating\TemplateNameParserInterface')->getMock(); return new TwigEngine($twig, $parser); } diff --git a/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php b/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php index 950c4d0810db0..35995dbd64518 100644 --- a/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php +++ b/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php @@ -61,10 +61,14 @@ public function extract($resource, MessageCatalogue $catalogue) try { $this->extractTemplate(file_get_contents($file->getPathname()), $catalogue); } catch (\Twig_Error $e) { - if ($file instanceof SplFileInfo) { - $e->setTemplateName($file->getRelativePathname()); - } elseif ($file instanceof \SplFileInfo) { - $e->setTemplateName($file->getRealPath() ?: $file->getPathname()); + if ($file instanceof \SplFileInfo) { + $path = $file->getRealPath() ?: $file->getPathname(); + $name = $file instanceof SplFileInfo ? $file->getRelativePathname() : $path; + if (method_exists($e, 'setSourceContext')) { + $e->setSourceContext(new \Twig_Source('', $name, $path)); + } else { + $e->setTemplateName($name); + } } throw $e; diff --git a/src/Symfony/Bridge/Twig/TwigEngine.php b/src/Symfony/Bridge/Twig/TwigEngine.php index 1ac9d0102e63b..760461b5be578 100644 --- a/src/Symfony/Bridge/Twig/TwigEngine.php +++ b/src/Symfony/Bridge/Twig/TwigEngine.php @@ -112,7 +112,7 @@ public function supports($name) * @param string|TemplateReferenceInterface|\Twig_Template $name A template name or an instance of * TemplateReferenceInterface or \Twig_Template * - * @return \Twig_TemplateInterface A \Twig_TemplateInterface instance + * @return \Twig_Template A \Twig_Template instance * * @throws \InvalidArgumentException if the template does not exist */ diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index fd73a7ea3d346..38949fad20792 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -20,9 +20,10 @@ "twig/twig": "~1.28|~2.0" }, "require-dev": { + "fig/link-util": "^1.0", "symfony/asset": "~2.8|~3.0", "symfony/finder": "~2.8|~3.0", - "symfony/form": "~3.0.4", + "symfony/form": "^3.2.7", "symfony/http-kernel": "~3.2", "symfony/polyfill-intl-icu": "~1.0", "symfony/routing": "~2.8|~3.0", @@ -34,7 +35,8 @@ "symfony/stopwatch": "~2.8|~3.0", "symfony/console": "~2.8|~3.0", "symfony/var-dumper": "~2.8.10|~3.1.4|~3.2", - "symfony/expression-language": "~2.8|~3.0" + "symfony/expression-language": "~2.8|~3.0", + "symfony/web-link": "~3.3" }, "suggest": { "symfony/finder": "", @@ -48,7 +50,8 @@ "symfony/security": "For using the SecurityExtension", "symfony/stopwatch": "For using the StopwatchExtension", "symfony/var-dumper": "For using the DumpExtension", - "symfony/expression-language": "For using the ExpressionExtension" + "symfony/expression-language": "For using the ExpressionExtension", + "symfony/web-link": "For using the WebLinkExtension" }, "autoload": { "psr-4": { "Symfony\\Bridge\\Twig\\": "" }, @@ -59,7 +62,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.3-dev" } } } diff --git a/src/Symfony/Bridge/Twig/phpunit.xml.dist b/src/Symfony/Bridge/Twig/phpunit.xml.dist index 10c0be1142712..642b7d19d8b70 100644 --- a/src/Symfony/Bridge/Twig/phpunit.xml.dist +++ b/src/Symfony/Bridge/Twig/phpunit.xml.dist @@ -5,6 +5,8 @@ backupGlobals="false" colors="true" bootstrap="vendor/autoload.php" + failOnRisky="true" + failOnWarning="true" > diff --git a/src/Symfony/Bundle/DebugBundle/LICENSE b/src/Symfony/Bundle/DebugBundle/LICENSE index 39fa189d2b5fc..207646a052dcd 100644 --- a/src/Symfony/Bundle/DebugBundle/LICENSE +++ b/src/Symfony/Bundle/DebugBundle/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014-2016 Fabien Potencier +Copyright (c) 2014-2017 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml b/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml index aada03eff87cf..ae7d91add15d4 100644 --- a/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml +++ b/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml @@ -14,7 +14,7 @@ - + %kernel.charset% null @@ -38,7 +38,7 @@ 0 - + diff --git a/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/Compiler/DumpDataCollectorPassTest.php b/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/Compiler/DumpDataCollectorPassTest.php index 4500fac7215b2..6f49c021260ed 100644 --- a/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/Compiler/DumpDataCollectorPassTest.php +++ b/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/Compiler/DumpDataCollectorPassTest.php @@ -11,13 +11,14 @@ namespace Symfony\Bundle\DebugBundle\Tests\DependencyInjection\Compiler; +use PHPUnit\Framework\TestCase; use Symfony\Bundle\DebugBundle\DependencyInjection\Compiler\DumpDataCollectorPass; use Symfony\Bundle\WebProfilerBundle\EventListener\WebDebugToolbarListener; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\HttpFoundation\RequestStack; -class DumpDataCollectorPassTest extends \PHPUnit_Framework_TestCase +class DumpDataCollectorPassTest extends TestCase { public function testProcessWithoutFileLinkFormatParameter() { diff --git a/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/DebugExtensionTest.php b/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/DebugExtensionTest.php index fc4e620e81a4c..9c03e9988224a 100644 --- a/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/DebugExtensionTest.php +++ b/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/DebugExtensionTest.php @@ -11,11 +11,12 @@ namespace Symfony\Bundle\DebugBundle\Tests\DependencyInjection; +use PHPUnit\Framework\TestCase; use Symfony\Bundle\DebugBundle\DependencyInjection\DebugExtension; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; -class DebugExtensionTest extends \PHPUnit_Framework_TestCase +class DebugExtensionTest extends TestCase { public function testLoadWithoutConfiguration() { diff --git a/src/Symfony/Bundle/DebugBundle/composer.json b/src/Symfony/Bundle/DebugBundle/composer.json index e5fe655296473..d6d7d0b33b16f 100644 --- a/src/Symfony/Bundle/DebugBundle/composer.json +++ b/src/Symfony/Bundle/DebugBundle/composer.json @@ -39,7 +39,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.3-dev" } } } diff --git a/src/Symfony/Bundle/DebugBundle/phpunit.xml.dist b/src/Symfony/Bundle/DebugBundle/phpunit.xml.dist index 90ec0a5dba514..3df3f74a7d9a5 100644 --- a/src/Symfony/Bundle/DebugBundle/phpunit.xml.dist +++ b/src/Symfony/Bundle/DebugBundle/phpunit.xml.dist @@ -5,6 +5,8 @@ backupGlobals="false" colors="true" bootstrap="vendor/autoload.php" + failOnRisky="true" + failOnWarning="true" > diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index efee66b41cecf..df80163243c1c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -1,6 +1,55 @@ CHANGELOG ========= +3.3.0 +----- + + * Not defining the `type` option of the `framework.workflows.*` configuration entries is deprecated. + The default value will be `state_machine` in Symfony 4.0. + * Deprecated the `CompilerDebugDumpPass` class + * [BC BREAK] Removed the "framework.trusted_proxies" configuration option and the corresponding "kernel.trusted_proxies" parameter + * Added a new new version strategy option called json_manifest_path + that allows you to use the `JsonManifestVersionStrategy`. + * Added `Symfony\Bundle\FrameworkBundle\Controller\AbstractController`. It provides + the same helpers as the `Controller` class, but does not allow accessing the dependency + injection container, in order to encourage explicit dependency declarations. + * Added support for the `controller.service_arguments` tag, for injecting services into controllers' actions + * Deprecated `cache:clear` with warmup (always call it with `--no-warmup`) + * Changed default configuration for + assets/forms/validation/translation/serialization/csrf from `canBeEnabled()` to + `canBeDisabled()` when Flex is used + * The server:* commands and their associated router files were moved to WebServerBundle + * Translation related services are not loaded anymore when the `framework.translator` option + is disabled. + * Added `GlobalVariables::getToken()` + * Deprecated `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConsoleCommandPass`. Use `Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass` instead. + * Added configurable paths for validation files + * Deprecated `SerializerPass`, use `Symfony\Component\Serializer\DependencyInjection\SerializerPass` instead + * Deprecated `FormPass`, use `Symfony\Component\Form\DependencyInjection\FormPass` instead + * Deprecated `SessionListener` + * Deprecated `TestSessionListener` + * Deprecated `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ConfigCachePass`. + Use `Symfony\Component\Console\DependencyInjection\ConfigCachePass` instead. + * Deprecated `PropertyInfoPass`, use `Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass` instead + * Deprecated extending `ConstraintValidatorFactory` + * Deprecated `ControllerArgumentValueResolverPass`. Use + `Symfony\Component\HttpKernel\DependencyInjection\ControllerArgumentValueResolverPass` instead + * Deprecated `RoutingResolverPass`, use `Symfony\Component\Routing\DependencyInjection\RoutingResolverPass` instead + * [BC BREAK] The `server:run`, `server:start`, `server:stop` and + `server:status` console commands have been moved to a dedicated bundle. + Require `symfony/web-server-bundle` in your composer.json and register + `Symfony\Bundle\WebServerBundle\WebServerBundle` in your AppKernel to use them. + * Added `$defaultLocale` as 3rd argument of `Translator::__construct()` + making `Translator` works with any PSR-11 container + * Added `framework.serializer.mapping` config option allowing to define custom + serialization mapping files and directories + * Deprecated `AddValidatorInitializersPass`, use + `Symfony\Component\Validator\DependencyInjection\AddValidatorInitializersPass` instead + * Deprecated `AddConstraintValidatorsPass`, use + `Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass` instead + * Deprecated `ValidateWorkflowsPass`, use + `Symfony\Component\Workflow\DependencyInjection\ValidateWorkflowsPass` instead + 3.2.0 ----- @@ -11,7 +60,7 @@ CHANGELOG * Removed `symfony/asset` from the list of required dependencies in `composer.json` * The `Resources/public/images/*` files have been removed. * The `Resources/public/css/*.css` files have been removed (they are now inlined in TwigBundle). - * Added possibility to prioritize form type extensions with `'priority'` attribute on tags `form.type_extension` + * Added possibility to prioritize form type extensions with `'priority'` attribute on tags `form.type_extension` 3.1.0 ----- diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AnnotationsCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AnnotationsCacheWarmer.php index 6d044676eb012..a6fb4ed095d2b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AnnotationsCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AnnotationsCacheWarmer.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\CacheWarmer; +use Doctrine\Common\Annotations\AnnotationException; use Doctrine\Common\Annotations\CachedReader; use Doctrine\Common\Annotations\Reader; use Psr\Cache\CacheItemPoolInterface; @@ -35,8 +36,8 @@ class AnnotationsCacheWarmer implements CacheWarmerInterface /** * @param Reader $annotationReader - * @param string $phpArrayFile The PHP file where annotations are cached. - * @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered annotations are cached. + * @param string $phpArrayFile The PHP file where annotations are cached + * @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered annotations are cached */ public function __construct(Reader $annotationReader, $phpArrayFile, CacheItemPoolInterface $fallbackPool) { @@ -67,8 +68,26 @@ public function warmUp($cacheDir) $arrayPool = new ArrayAdapter(0, false); $reader = new CachedReader($this->annotationReader, new DoctrineProvider($arrayPool)); - foreach ($annotatedClasses as $class) { - $this->readAllComponents($reader, $class); + spl_autoload_register(array($adapter, 'throwOnRequiredClass')); + try { + foreach ($annotatedClasses as $class) { + try { + $this->readAllComponents($reader, $class); + } catch (\ReflectionException $e) { + // ignore failing reflection + } catch (AnnotationException $e) { + /* + * Ignore any AnnotationException to not break the cache warming process if an Annotation is badly + * configured or could not be found / read / etc. + * + * In particular cases, an Annotation in your code can be used and defined only for a specific + * environment but is always added to the annotations.map file by some Symfony default behaviors, + * and you always end up with a not found Annotation. + */ + } + } + } finally { + spl_autoload_unregister(array($adapter, 'throwOnRequiredClass')); } $values = $arrayPool->getValues(); diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ClassCacheCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ClassCacheCacheWarmer.php index b235c65136555..1240f14c5b749 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ClassCacheCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ClassCacheCacheWarmer.php @@ -18,6 +18,8 @@ * Generates the Class Cache (classes.php) file. * * @author Tugdual Saunier + * + * @deprecated since version 3.3, to be removed in 4.0. */ class ClassCacheCacheWarmer implements CacheWarmerInterface { @@ -25,6 +27,10 @@ class ClassCacheCacheWarmer implements CacheWarmerInterface public function __construct(array $declaredClasses = null) { + if (PHP_VERSION_ID >= 70000) { + @trigger_error('The '.__CLASS__.' class is deprecated since version 3.3 and will be removed in 4.0.', E_USER_DEPRECATED); + } + $this->declaredClasses = $declaredClasses; } diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/SerializerCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/SerializerCacheWarmer.php index 23e6142be4993..c017f51268b3d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/SerializerCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/SerializerCacheWarmer.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\CacheWarmer; +use Doctrine\Common\Annotations\AnnotationException; use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\Cache\Adapter\AdapterInterface; use Symfony\Component\Cache\Adapter\ArrayAdapter; @@ -36,9 +37,9 @@ class SerializerCacheWarmer implements CacheWarmerInterface private $fallbackPool; /** - * @param LoaderInterface[] $loaders The serializer metadata loaders. - * @param string $phpArrayFile The PHP file where metadata are cached. - * @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered metadata are cached. + * @param LoaderInterface[] $loaders The serializer metadata loaders + * @param string $phpArrayFile The PHP file where metadata are cached + * @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered metadata are cached */ public function __construct(array $loaders, $phpArrayFile, CacheItemPoolInterface $fallbackPool) { @@ -64,10 +65,21 @@ public function warmUp($cacheDir) $metadataFactory = new CacheClassMetadataFactory(new ClassMetadataFactory(new LoaderChain($this->loaders)), $arrayPool); - foreach ($this->extractSupportedLoaders($this->loaders) as $loader) { - foreach ($loader->getMappedClasses() as $mappedClass) { - $metadataFactory->getMetadataFor($mappedClass); + spl_autoload_register(array($adapter, 'throwOnRequiredClass')); + try { + foreach ($this->extractSupportedLoaders($this->loaders) as $loader) { + foreach ($loader->getMappedClasses() as $mappedClass) { + try { + $metadataFactory->getMetadataFor($mappedClass); + } catch (\ReflectionException $e) { + // ignore failing reflection + } catch (AnnotationException $e) { + // ignore failing annotations + } + } } + } finally { + spl_autoload_unregister(array($adapter, 'throwOnRequiredClass')); } $values = $arrayPool->getValues(); diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplateFinder.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplateFinder.php index 1e229849824ac..afda1191777b8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplateFinder.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplateFinder.php @@ -14,6 +14,7 @@ use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\Finder\Finder; use Symfony\Component\Templating\TemplateNameParserInterface; +use Symfony\Component\Templating\TemplateReferenceInterface; use Symfony\Component\HttpKernel\Bundle\BundleInterface; /** diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplatePathsCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplatePathsCacheWarmer.php index e7db48a010fa9..4cb9f41b29556 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplatePathsCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplatePathsCacheWarmer.php @@ -35,7 +35,6 @@ public function __construct(TemplateFinderInterface $finder, TemplateLocator $lo { $this->finder = $finder; $this->locator = $locator; - $this->filesystem = new Filesystem(); } /** @@ -45,10 +44,11 @@ public function __construct(TemplateFinderInterface $finder, TemplateLocator $lo */ public function warmUp($cacheDir) { + $filesystem = new Filesystem(); $templates = array(); foreach ($this->finder->findAllTemplates() as $template) { - $templates[$template->getLogicalName()] = rtrim($this->filesystem->makePathRelative($this->locator->locate($template), $cacheDir), '/'); + $templates[$template->getLogicalName()] = rtrim($filesystem->makePathRelative($this->locator->locate($template), $cacheDir), '/'); } $templates = str_replace("' => '", "' => __DIR__.'/", var_export($templates, true)); diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ValidatorCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ValidatorCacheWarmer.php index ce5104dc41cc5..81291d772fbf0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ValidatorCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ValidatorCacheWarmer.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\CacheWarmer; +use Doctrine\Common\Annotations\AnnotationException; use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\Cache\Adapter\AdapterInterface; use Symfony\Component\Cache\Adapter\ArrayAdapter; @@ -38,8 +39,8 @@ class ValidatorCacheWarmer implements CacheWarmerInterface /** * @param ValidatorBuilderInterface $validatorBuilder - * @param string $phpArrayFile The PHP file where metadata are cached. - * @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered metadata are cached. + * @param string $phpArrayFile The PHP file where metadata are cached + * @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered metadata are cached */ public function __construct(ValidatorBuilderInterface $validatorBuilder, $phpArrayFile, CacheItemPoolInterface $fallbackPool) { @@ -66,16 +67,27 @@ public function warmUp($cacheDir) $loaders = $this->validatorBuilder->getLoaders(); $metadataFactory = new LazyLoadingMetadataFactory(new LoaderChain($loaders), new Psr6Cache($arrayPool)); - foreach ($this->extractSupportedLoaders($loaders) as $loader) { - foreach ($loader->getMappedClasses() as $mappedClass) { - if ($metadataFactory->hasMetadataFor($mappedClass)) { - $metadataFactory->getMetadataFor($mappedClass); + spl_autoload_register(array($adapter, 'throwOnRequiredClass')); + try { + foreach ($this->extractSupportedLoaders($loaders) as $loader) { + foreach ($loader->getMappedClasses() as $mappedClass) { + try { + if ($metadataFactory->hasMetadataFor($mappedClass)) { + $metadataFactory->getMetadataFor($mappedClass); + } + } catch (\ReflectionException $e) { + // ignore failing reflection + } catch (AnnotationException $e) { + // ignore failing annotations + } } } + } finally { + spl_autoload_unregister(array($adapter, 'throwOnRequiredClass')); } $values = $arrayPool->getValues(); - $adapter->warmUp($values); + $adapter->warmUp(array_filter($values)); foreach ($values as $k => $v) { $item = $this->fallbackPool->getItem($k); diff --git a/src/Symfony/Bundle/FrameworkBundle/Client.php b/src/Symfony/Bundle/FrameworkBundle/Client.php index 04fbd3ab0fab2..2238ae859498d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Client.php +++ b/src/Symfony/Bundle/FrameworkBundle/Client.php @@ -163,34 +163,36 @@ protected function getScript($request) { $kernel = str_replace("'", "\\'", serialize($this->kernel)); $request = str_replace("'", "\\'", serialize($request)); + $errorReporting = error_reporting(); - $r = new \ReflectionObject($this->kernel); + $requires = ''; + foreach (get_declared_classes() as $class) { + if (0 === strpos($class, 'ComposerAutoloaderInit')) { + $r = new \ReflectionClass($class); + $file = dirname(dirname($r->getFileName())).'/autoload.php'; + if (file_exists($file)) { + $requires .= "require_once '".str_replace("'", "\\'", $file)."';\n"; + } + } + } - $autoloader = dirname($r->getFileName()).'/autoload.php'; - if (is_file($autoloader)) { - $autoloader = str_replace("'", "\\'", $autoloader); - } else { - $autoloader = ''; + if (!$requires) { + throw new \RuntimeException('Composer autoloader not found.'); } - $path = str_replace("'", "\\'", $r->getFileName()); + $requires .= "require_once '".str_replace("'", "\\'", (new \ReflectionObject($this->kernel))->getFileName())."';\n"; $profilerCode = ''; if ($this->profiler) { $profilerCode = '$kernel->getContainer()->get(\'profiler\')->enable();'; } - $errorReporting = error_reporting(); - $code = <<boot(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php new file mode 100644 index 0000000000000..cb6f43a43c106 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Helper\TableSeparator; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\HttpKernel\KernelInterface; + +/** + * A console command to display information about the current installation. + * + * @author Roland Franssen + */ +class AboutCommand extends ContainerAwareCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('about') + ->setDescription('Displays information about the current project') + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + + /** @var $kernel KernelInterface */ + $kernel = $this->getContainer()->get('kernel'); + $baseDir = realpath($kernel->getRootDir().DIRECTORY_SEPARATOR.'..'); + + $io->table(array(), array( + array('Symfony'), + new TableSeparator(), + array('Version', Kernel::VERSION), + array('End of maintenance', Kernel::END_OF_MAINTENANCE.(self::isExpired(Kernel::END_OF_MAINTENANCE) ? ' Expired' : '')), + array('End of life', Kernel::END_OF_LIFE.(self::isExpired(Kernel::END_OF_LIFE) ? ' Expired' : '')), + new TableSeparator(), + array('Kernel'), + new TableSeparator(), + array('Type', get_class($kernel)), + array('Name', $kernel->getName()), + array('Environment', $kernel->getEnvironment()), + array('Debug', $kernel->isDebug() ? 'true' : 'false'), + array('Charset', $kernel->getCharset()), + array('Root directory', self::formatPath($kernel->getRootDir(), $baseDir)), + array('Cache directory', self::formatPath($kernel->getCacheDir(), $baseDir).' ('.self::formatFileSize($kernel->getCacheDir()).')'), + array('Log directory', self::formatPath($kernel->getLogDir(), $baseDir).' ('.self::formatFileSize($kernel->getLogDir()).')'), + new TableSeparator(), + array('PHP'), + new TableSeparator(), + array('Version', PHP_VERSION), + array('Architecture', (PHP_INT_SIZE * 8).' bits'), + array('Intl locale', \Locale::getDefault() ?: 'n/a'), + array('Timezone', date_default_timezone_get().' ('.(new \DateTime())->format(\DateTime::W3C).')'), + array('OPcache', extension_loaded('Zend OPcache') && ini_get('opcache.enable') ? 'true' : 'false'), + array('APCu', extension_loaded('apcu') && ini_get('apc.enabled') ? 'true' : 'false'), + array('Xdebug', extension_loaded('xdebug') ? 'true' : 'false'), + )); + } + + private static function formatPath($path, $baseDir = null) + { + return null !== $baseDir ? preg_replace('~^'.preg_quote($baseDir, '~').'~', '.', $path) : $path; + } + + private static function formatFileSize($path) + { + if (is_file($path)) { + $size = filesize($path) ?: 0; + } else { + $size = 0; + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS | \RecursiveDirectoryIterator::FOLLOW_SYMLINKS)) as $file) { + $size += $file->getSize(); + } + } + + return Helper::formatMemory($size); + } + + private static function isExpired($date) + { + $date = \DateTime::createFromFormat('m/Y', $date); + + return false !== $date && new \DateTime() > $date->modify('last day of this month 23:59:59'); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php index 9f36c8d714d72..feb966d850939 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php @@ -82,7 +82,13 @@ protected function execute(InputInterface $input, OutputInterface $output) $targetArg = rtrim($input->getArgument('target'), '/'); if (!is_dir($targetArg)) { - throw new \InvalidArgumentException(sprintf('The target directory "%s" does not exist.', $input->getArgument('target'))); + $appRoot = $this->getContainer()->getParameter('kernel.root_dir').'/..'; + + $targetArg = $appRoot.'/'.$targetArg; + + if (!is_dir($targetArg)) { + throw new \InvalidArgumentException(sprintf('The target directory "%s" does not exist.', $input->getArgument('target'))); + } } $this->filesystem = $this->getContainer()->get('filesystem'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php index f926b4153bcd5..49284270e6e34 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php @@ -54,7 +54,6 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $outputIsVerbose = $output->isVerbose(); $io = new SymfonyStyle($input, $output); $realCacheDir = $this->getContainer()->getParameter('kernel.cache_dir'); @@ -78,47 +77,59 @@ protected function execute(InputInterface $input, OutputInterface $output) if ($input->getOption('no-warmup')) { $filesystem->rename($realCacheDir, $oldCacheDir); } else { - // the warmup cache dir name must have the same length than the real one - // to avoid the many problems in serialized resources files - $realCacheDir = realpath($realCacheDir); - $warmupDir = substr($realCacheDir, 0, -1).('_' === substr($realCacheDir, -1) ? '-' : '_'); - - if ($filesystem->exists($warmupDir)) { - if ($outputIsVerbose) { - $io->comment('Clearing outdated warmup directory...'); - } - $filesystem->remove($warmupDir); - } - - if ($outputIsVerbose) { - $io->comment('Warming up cache...'); - } - $this->warmup($warmupDir, $realCacheDir, !$input->getOption('no-optional-warmers')); + @trigger_error('Calling cache:clear without the --no-warmup option is deprecated since version 3.3. Cache warmup should be done with the cache:warmup command instead.', E_USER_DEPRECATED); - $filesystem->rename($realCacheDir, $oldCacheDir); - if ('\\' === DIRECTORY_SEPARATOR) { - sleep(1); // workaround for Windows PHP rename bug - } - $filesystem->rename($warmupDir, $realCacheDir); + $this->warmupCache($input, $output, $realCacheDir, $oldCacheDir); } - if ($outputIsVerbose) { + if ($output->isVerbose()) { $io->comment('Removing old cache directory...'); } $filesystem->remove($oldCacheDir); - if ($outputIsVerbose) { + if ($output->isVerbose()) { $io->comment('Finished'); } $io->success(sprintf('Cache for the "%s" environment (debug=%s) was successfully cleared.', $kernel->getEnvironment(), var_export($kernel->isDebug(), true))); } + private function warmupCache(InputInterface $input, OutputInterface $output, $realCacheDir, $oldCacheDir) + { + $filesystem = $this->getContainer()->get('filesystem'); + $io = new SymfonyStyle($input, $output); + + // the warmup cache dir name must have the same length than the real one + // to avoid the many problems in serialized resources files + $realCacheDir = realpath($realCacheDir); + $warmupDir = substr($realCacheDir, 0, -1).('_' === substr($realCacheDir, -1) ? '-' : '_'); + + if ($filesystem->exists($warmupDir)) { + if ($output->isVerbose()) { + $io->comment('Clearing outdated warmup directory...'); + } + $filesystem->remove($warmupDir); + } + + if ($output->isVerbose()) { + $io->comment('Warming up cache...'); + } + $this->warmup($warmupDir, $realCacheDir, !$input->getOption('no-optional-warmers')); + + $filesystem->rename($realCacheDir, $oldCacheDir); + if ('\\' === DIRECTORY_SEPARATOR) { + sleep(1); // workaround for Windows PHP rename bug + } + $filesystem->rename($warmupDir, $realCacheDir); + } + /** * @param string $warmupDir * @param string $realCacheDir * @param bool $enableOptionalWarmers + * + * @internal to be removed in 4.0 */ protected function warmup($warmupDir, $realCacheDir, $enableOptionalWarmers = true) { @@ -183,6 +194,8 @@ protected function warmup($warmupDir, $realCacheDir, $enableOptionalWarmers = tr * @param string $warmupDir * * @return KernelInterface + * + * @internal to be removed in 4.0 */ protected function getTempKernel(KernelInterface $parent, $namespace, $parentClass, $warmupDir) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolClearCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolClearCommand.php index 82934e1845fea..3ee9f086aedbc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolClearCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolClearCommand.php @@ -33,7 +33,7 @@ protected function configure() $this ->setName('cache:pool:clear') ->setDefinition(array( - new InputArgument('pools', InputArgument::IS_ARRAY, 'A list of cache pools or cache pool clearers'), + new InputArgument('pools', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'A list of cache pools or cache pool clearers'), )) ->setDescription('Clears cache pools') ->setHelp(<<<'EOF' @@ -55,16 +55,21 @@ protected function execute(InputInterface $input, OutputInterface $output) $clearers = array(); $container = $this->getContainer(); $cacheDir = $container->getParameter('kernel.cache_dir'); + $globalClearer = $container->get('cache.global_clearer'); foreach ($input->getArgument('pools') as $id) { - $pool = $container->get($id); - - if ($pool instanceof CacheItemPoolInterface) { - $pools[$id] = $pool; - } elseif ($pool instanceof Psr6CacheClearer) { - $clearers[$id] = $pool; + if ($globalClearer->hasPool($id)) { + $pools[$id] = $id; } else { - throw new \InvalidArgumentException(sprintf('"%s" is not a cache pool nor a cache clearer.', $id)); + $pool = $container->get($id); + + if ($pool instanceof CacheItemPoolInterface) { + $pools[$id] = $pool; + } elseif ($pool instanceof Psr6CacheClearer) { + $clearers[$id] = $pool; + } else { + throw new \InvalidArgumentException(sprintf('"%s" is not a cache pool nor a cache clearer.', $id)); + } } } @@ -75,7 +80,12 @@ protected function execute(InputInterface $input, OutputInterface $output) foreach ($pools as $id => $pool) { $io->comment(sprintf('Clearing cache pool: %s', $id)); - $pool->clear(); + + if ($pool instanceof CacheItemPoolInterface) { + $pool->clear(); + } else { + $globalClearer->clearPool($id); + } } $io->success('Cache was successfully cleared.'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php index 22811c6558894..924bd8e92f659 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php @@ -62,11 +62,12 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { $io = new SymfonyStyle($input, $output); + $errorIo = $io->getErrorStyle(); if (null === $name = $input->getArgument('name')) { - $this->listBundles($io); - $io->comment('Provide the name of a bundle as the first argument of this command to dump its configuration. (e.g. debug:config FrameworkBundle)'); - $io->comment('For dumping a specific option, add its path as the second argument of this command. (e.g. debug:config FrameworkBundle serializer to dump the framework.serializer configuration)'); + $this->listBundles($errorIo); + $errorIo->comment('Provide the name of a bundle as the first argument of this command to dump its configuration. (e.g. debug:config FrameworkBundle)'); + $errorIo->comment('For dumping a specific option, add its path as the second argument of this command. (e.g. debug:config FrameworkBundle serializer to dump the framework.serializer configuration)'); return; } @@ -80,10 +81,10 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->validateConfiguration($extension, $configuration); - $configs = $container->getParameterBag()->resolveValue($configs); + $configs = $container->resolveEnvPlaceholders($container->getParameterBag()->resolveValue($configs)); $processor = new Processor(); - $config = $processor->processConfiguration($configuration, $configs); + $config = $container->resolveEnvPlaceholders($container->getParameterBag()->resolveValue($processor->processConfiguration($configuration, $configs))); if (null === $path = $input->getArgument('path')) { $io->title( @@ -98,7 +99,7 @@ protected function execute(InputInterface $input, OutputInterface $output) try { $config = $this->getConfigForPath($config, $path, $extensionAlias); } catch (LogicException $e) { - $io->error($e->getMessage()); + $errorIo->error($e->getMessage()); return; } @@ -130,7 +131,7 @@ private function compileContainer() * * @return mixed */ - private function getConfigForPath(array $config = array(), $path, $alias) + private function getConfigForPath(array $config, $path, $alias) { $steps = explode('.', $path); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php index dfa604f7a85ae..476813fc3621e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php @@ -37,6 +37,7 @@ protected function configure() ->setName('config:dump-reference') ->setDefinition(array( new InputArgument('name', InputArgument::OPTIONAL, 'The Bundle name or the extension alias'), + new InputArgument('path', InputArgument::OPTIONAL, 'The configuration option path'), new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (yaml or xml)', 'yaml'), )) ->setDescription('Dumps the default configuration for an extension') @@ -55,6 +56,10 @@ protected function configure() php %command.full_name% FrameworkBundle --format=xml +For dumping a specific option, add its path as second argument (only available for the yaml format): + + php %command.full_name% framework profiler.matcher + EOF ) ; @@ -68,10 +73,14 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { $io = new SymfonyStyle($input, $output); + $errorIo = $io->getErrorStyle(); if (null === $name = $input->getArgument('name')) { - $this->listBundles($io); - $io->comment('Provide the name of a bundle as the first argument of this command to dump its default configuration. (e.g. config:dump-reference FrameworkBundle)'); + $this->listBundles($errorIo); + $errorIo->comment(array( + 'Provide the name of a bundle as the first argument of this command to dump its default configuration. (e.g. config:dump-reference FrameworkBundle)', + 'For dumping a specific option, add its path as the second argument of this command. (e.g. config:dump-reference FrameworkBundle profiler.matcher to dump the framework.profiler.matcher configuration)', + )); return; } @@ -82,13 +91,26 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->validateConfiguration($extension, $configuration); + $format = $input->getOption('format'); + $path = $input->getArgument('path'); + + if ($path !== null && 'yaml' !== $format) { + $errorIo->error('The "path" option is only available for the "yaml" format.'); + + return 1; + } + if ($name === $extension->getAlias()) { $message = sprintf('Default configuration for extension with alias: "%s"', $name); } else { $message = sprintf('Default configuration for "%s"', $name); } - switch ($input->getOption('format')) { + if ($path !== null) { + $message .= sprintf(' at path "%s"', $path); + } + + switch ($format) { case 'yaml': $io->writeln(sprintf('# %s', $message)); $dumper = new YamlReferenceDumper(); @@ -102,6 +124,6 @@ protected function execute(InputInterface $input, OutputInterface $output) throw new \InvalidArgumentException('Only the yaml and xml formats are supported.'); } - $io->writeln($dumper->dump($configuration, $extension->getNamespace())); + $io->writeln(null === $path ? $dumper->dump($configuration, $extension->getNamespace()) : $dumper->dumpAtPath($configuration, $path)); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php index 981df241cd3a1..8e8bf8ca3c0ff 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php @@ -19,6 +19,7 @@ use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\Config\FileLocator; /** @@ -43,6 +44,7 @@ protected function configure() ->setDefinition(array( new InputArgument('name', InputArgument::OPTIONAL, 'A service name (foo)'), new InputOption('show-private', null, InputOption::VALUE_NONE, 'Used to show public *and* private services'), + new InputOption('show-arguments', null, InputOption::VALUE_NONE, 'Used to show arguments in services'), new InputOption('tag', null, InputOption::VALUE_REQUIRED, 'Shows all services with a specific tag'), new InputOption('tags', null, InputOption::VALUE_NONE, 'Displays tagged services for an application'), new InputOption('parameter', null, InputOption::VALUE_REQUIRED, 'Displays a specific parameter for an application'), @@ -92,11 +94,17 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { $io = new SymfonyStyle($input, $output); + $errorIo = $io->getErrorStyle(); + $this->validateInput($input); $object = $this->getContainerBuilder(); if ($input->getOption('parameters')) { - $object = $object->getParameterBag(); + $parameters = array(); + foreach ($object->getParameterBag()->all() as $k => $v) { + $parameters[$k] = $object->resolveEnvPlaceholders($v); + } + $object = new ParameterBag($parameters); $options = array(); } elseif ($parameter = $input->getOption('parameter')) { $options = array('parameter' => $parameter); @@ -105,7 +113,7 @@ protected function execute(InputInterface $input, OutputInterface $output) } elseif ($tag = $input->getOption('tag')) { $options = array('tag' => $tag, 'show_private' => $input->getOption('show-private')); } elseif ($name = $input->getArgument('name')) { - $name = $this->findProperServiceName($input, $io, $object, $name); + $name = $this->findProperServiceName($input, $errorIo, $object, $name); $options = array('id' => $name); } else { $options = array('show_private' => $input->getOption('show-private')); @@ -113,17 +121,18 @@ protected function execute(InputInterface $input, OutputInterface $output) $helper = new DescriptorHelper(); $options['format'] = $input->getOption('format'); + $options['show_arguments'] = $input->getOption('show-arguments'); $options['raw_text'] = $input->getOption('raw'); $options['output'] = $io; - $helper->describe($output, $object, $options); + $helper->describe($io, $object, $options); if (!$input->getArgument('name') && !$input->getOption('tag') && !$input->getOption('parameter') && $input->isInteractive()) { if ($input->getOption('tags')) { - $io->comment('To search for a specific tag, re-run this command with a search term. (e.g. debug:container --tag=form.type)'); + $errorIo->comment('To search for a specific tag, re-run this command with a search term. (e.g. debug:container --tag=form.type)'); } elseif ($input->getOption('parameters')) { - $io->comment('To search for a specific parameter, re-run this command with a search term. (e.g. debug:container --parameter=kernel.debug)'); + $errorIo->comment('To search for a specific parameter, re-run this command with a search term. (e.g. debug:container --parameter=kernel.debug)'); } else { - $io->comment('To search for a specific service, re-run this command with a search term. (e.g. debug:container log)'); + $errorIo->comment('To search for a specific service, re-run this command with a search term. (e.g. debug:container log)'); } } } @@ -168,11 +177,11 @@ protected function getContainerBuilder() } if (!$this->getApplication()->getKernel()->isDebug()) { - throw new \LogicException(sprintf('Debug information about the container is only available in debug mode.')); + throw new \LogicException('Debug information about the container is only available in debug mode.'); } if (!is_file($cachedFile = $this->getContainer()->getParameter('debug.container.dump'))) { - throw new \LogicException(sprintf('Debug information about the container could not be found. Please clear the cache and try again.')); + throw new \LogicException('Debug information about the container could not be found. Please clear the cache and try again.'); } $container = new ContainerBuilder(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/EventDispatcherDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/EventDispatcherDebugCommand.php index 2eb310ddf1701..ef1c3017fca28 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/EventDispatcherDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/EventDispatcherDebugCommand.php @@ -65,7 +65,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $options = array(); if ($event = $input->getArgument('event')) { if (!$dispatcher->hasListeners($event)) { - $io->warning(sprintf('The event "%s" does not have any registered listeners.', $event)); + $io->getErrorStyle()->warning(sprintf('The event "%s" does not have any registered listeners.', $event)); return; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php index 97707e8798b59..22f0bc6795d5b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php @@ -17,6 +17,7 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Routing\Route; @@ -85,13 +86,14 @@ protected function execute(InputInterface $input, OutputInterface $output) throw new \InvalidArgumentException(sprintf('The route "%s" does not exist.', $name)); } - $this->convertController($route); + $callable = $this->extractCallable($route); $helper->describe($io, $route, array( 'format' => $input->getOption('format'), 'raw_text' => $input->getOption('raw'), 'name' => $name, 'output' => $io, + 'callable' => $callable, )); } else { foreach ($routes as $route) { @@ -109,12 +111,38 @@ protected function execute(InputInterface $input, OutputInterface $output) private function convertController(Route $route) { - $nameParser = $this->getContainer()->get('controller_name_converter'); if ($route->hasDefault('_controller')) { + $nameParser = $this->getContainer()->get('controller_name_converter'); try { $route->setDefault('_controller', $nameParser->build($route->getDefault('_controller'))); } catch (\InvalidArgumentException $e) { } } } + + private function extractCallable(Route $route) + { + if (!$route->hasDefault('_controller')) { + return; + } + + $controller = $route->getDefault('_controller'); + + if (1 === substr_count($controller, ':')) { + list($service, $method) = explode(':', $controller); + try { + return sprintf('%s::%s', get_class($this->getContainer()->get($service)), $method); + } catch (ServiceNotFoundException $e) { + } + } + + $nameParser = $this->getContainer()->get('controller_name_converter'); + try { + $shortNotation = $nameParser->build($controller); + $route->setDefault('_controller', $shortNotation); + + return $controller; + } catch (\InvalidArgumentException $e) { + } + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ServerCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ServerCommand.php deleted file mode 100644 index ccfa5dde6b7e8..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ServerCommand.php +++ /dev/null @@ -1,69 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Command; - -/** - * Base methods for commands related to PHP's built-in web server. - * - * @author Christian Flothmann - */ -abstract class ServerCommand extends ContainerAwareCommand -{ - /** - * {@inheritdoc} - */ - public function isEnabled() - { - if (defined('HHVM_VERSION')) { - return false; - } - - if (!class_exists('Symfony\Component\Process\Process')) { - return false; - } - - return parent::isEnabled(); - } - - /** - * Determines the name of the lock file for a particular PHP web server process. - * - * @param string $address An address/port tuple - * - * @return string The filename - */ - protected function getLockFile($address) - { - return sys_get_temp_dir().'/'.strtr($address, '.:', '--').'.pid'; - } - - protected function isOtherServerProcessRunning($address) - { - $lockFile = $this->getLockFile($address); - - if (file_exists($lockFile)) { - return true; - } - - list($hostname, $port) = explode(':', $address); - - $fp = @fsockopen($hostname, $port, $errno, $errstr, 5); - - if (false !== $fp) { - fclose($fp); - - return true; - } - - return false; - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ServerRunCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ServerRunCommand.php deleted file mode 100644 index e7dec312f99ba..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ServerRunCommand.php +++ /dev/null @@ -1,175 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Command; - -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Output\ConsoleOutputInterface; -use Symfony\Component\Console\Style\SymfonyStyle; -use Symfony\Component\Process\PhpExecutableFinder; -use Symfony\Component\Process\Process; -use Symfony\Component\Process\ProcessBuilder; -use Symfony\Component\Process\Exception\RuntimeException; - -/** - * Runs Symfony application using PHP built-in web server. - * - * @author Michał Pipa - */ -class ServerRunCommand extends ServerCommand -{ - /** - * {@inheritdoc} - */ - protected function configure() - { - $this - ->setDefinition(array( - new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1'), - new InputOption('port', 'p', InputOption::VALUE_REQUIRED, 'Address port number', '8000'), - new InputOption('docroot', 'd', InputOption::VALUE_REQUIRED, 'Document root', null), - new InputOption('router', 'r', InputOption::VALUE_REQUIRED, 'Path to custom router script'), - )) - ->setName('server:run') - ->setDescription('Runs PHP built-in web server') - ->setHelp(<<<'EOF' -The %command.name% runs PHP built-in web server: - - %command.full_name% - -To change default bind address and port use the address argument: - - %command.full_name% 127.0.0.1:8080 - -To change default docroot directory use the --docroot option: - - %command.full_name% --docroot=htdocs/ - -If you have custom docroot directory layout, you can specify your own -router script using --router option: - - %command.full_name% --router=app/config/router.php - -Specifing a router script is required when the used environment is not "dev", -"prod", or "test". - -See also: http://www.php.net/manual/en/features.commandline.webserver.php - -EOF - ) - ; - } - - /** - * {@inheritdoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $io = new SymfonyStyle($input, $output); - $documentRoot = $input->getOption('docroot'); - - if (null === $documentRoot) { - $documentRoot = $this->getContainer()->getParameter('kernel.root_dir').'/../web'; - } - - if (!is_dir($documentRoot)) { - $io->error(sprintf('The given document root directory "%s" does not exist', $documentRoot)); - - return 1; - } - - $env = $this->getContainer()->getParameter('kernel.environment'); - $address = $input->getArgument('address'); - - if (false === strpos($address, ':')) { - $address = $address.':'.$input->getOption('port'); - } - - if ($this->isOtherServerProcessRunning($address)) { - $io->error(sprintf('A process is already listening on http://%s.', $address)); - - return 1; - } - - if ('prod' === $env) { - $io->error('Running PHP built-in server in production environment is NOT recommended!'); - } - - $io->success(sprintf('Server running on http://%s', $address)); - $io->comment('Quit the server with CONTROL-C.'); - - if (null === $builder = $this->createPhpProcessBuilder($io, $address, $input->getOption('router'), $env)) { - return 1; - } - - $builder->setWorkingDirectory($documentRoot); - $builder->setTimeout(null); - $process = $builder->getProcess(); - $callback = null; - - if (OutputInterface::VERBOSITY_NORMAL > $output->getVerbosity()) { - $process->disableOutput(); - } else { - try { - $process->setTty(true); - } catch (RuntimeException $e) { - $callback = function ($type, $buffer) use ($output) { - if (Process::ERR === $type && $output instanceof ConsoleOutputInterface) { - $output = $output->getErrorOutput(); - } - $output->write($buffer, false, OutputInterface::OUTPUT_RAW); - }; - } - } - $process->run($callback); - - if (!$process->isSuccessful()) { - $errorMessages = array('Built-in server terminated unexpectedly.'); - - if ($process->isOutputDisabled()) { - $errorMessages[] = 'Run the command again with -v option for more details.'; - } - - $io->error($errorMessages); - } - - return $process->getExitCode(); - } - - private function createPhpProcessBuilder(SymfonyStyle $io, $address, $router, $env) - { - $router = $router ?: $this - ->getContainer() - ->get('kernel') - ->locateResource(sprintf('@FrameworkBundle/Resources/config/router_%s.php', $env)) - ; - - if (!file_exists($router)) { - $io->error(sprintf('The given router script "%s" does not exist.', $router)); - - return; - } - - $router = realpath($router); - $finder = new PhpExecutableFinder(); - - if (false === $binary = $finder->find()) { - $io->error('Unable to find PHP binary to run server.'); - - return; - } - - return new ProcessBuilder(array($binary, '-S', $address, $router)); - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php deleted file mode 100644 index 5e2f273ac8784..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php +++ /dev/null @@ -1,234 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Command; - -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Style\SymfonyStyle; -use Symfony\Component\Process\PhpExecutableFinder; -use Symfony\Component\Process\Process; - -/** - * Runs PHP's built-in web server in a background process. - * - * @author Christian Flothmann - */ -class ServerStartCommand extends ServerCommand -{ - /** - * {@inheritdoc} - */ - protected function configure() - { - $this - ->setDefinition(array( - new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1'), - new InputOption('port', 'p', InputOption::VALUE_REQUIRED, 'Address port number', '8000'), - new InputOption('docroot', 'd', InputOption::VALUE_REQUIRED, 'Document root', null), - new InputOption('router', 'r', InputOption::VALUE_REQUIRED, 'Path to custom router script'), - new InputOption('force', 'f', InputOption::VALUE_NONE, 'Force web server startup'), - )) - ->setName('server:start') - ->setDescription('Starts PHP built-in web server in the background') - ->setHelp(<<<'EOF' -The %command.name% runs PHP's built-in web server: - - php %command.full_name% - -To change the default bind address and the default port use the address argument: - - php %command.full_name% 127.0.0.1:8080 - -To change the default document root directory use the --docroot option: - - php %command.full_name% --docroot=htdocs/ - -If you have a custom document root directory layout, you can specify your own -router script using the --router option: - - php %command.full_name% --router=app/config/router.php - -Specifying a router script is required when the used environment is not "dev" or -"prod". - -See also: http://www.php.net/manual/en/features.commandline.webserver.php - -EOF - ) - ; - } - - /** - * {@inheritdoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $io = new SymfonyStyle($input, $cliOutput = $output); - - if (!extension_loaded('pcntl')) { - $io->error(array( - 'This command needs the pcntl extension to run.', - 'You can either install it or use the "server:run" command instead to run the built-in web server.', - )); - - if ($io->ask('Do you want to execute server:run immediately? [Yn] ', true)) { - $command = $this->getApplication()->find('server:run'); - - return $command->run($input, $cliOutput); - } - - return 1; - } - - $documentRoot = $input->getOption('docroot'); - - if (null === $documentRoot) { - $documentRoot = $this->getContainer()->getParameter('kernel.root_dir').'/../web'; - } - - if (!is_dir($documentRoot)) { - $io->error(sprintf('The given document root directory "%s" does not exist.', $documentRoot)); - - return 1; - } - - $env = $this->getContainer()->getParameter('kernel.environment'); - - if (false === $router = $this->determineRouterScript($input->getOption('router'), $env, $io)) { - return 1; - } - - $address = $input->getArgument('address'); - - if (false === strpos($address, ':')) { - $address = $address.':'.$input->getOption('port'); - } - - if (!$input->getOption('force') && $this->isOtherServerProcessRunning($address)) { - $io->error(array( - sprintf('A process is already listening on http://%s.', $address), - 'Use the --force option if the server process terminated unexpectedly to start a new web server process.', - )); - - return 1; - } - - if ('prod' === $env) { - $io->error('Running PHP built-in server in production environment is NOT recommended!'); - } - - $pid = pcntl_fork(); - - if ($pid < 0) { - $io->error('Unable to start the server process.'); - - return 1; - } - - if ($pid > 0) { - $io->success(sprintf('Web server listening on http://%s', $address)); - - return; - } - - if (posix_setsid() < 0) { - $io->error('Unable to set the child process as session leader'); - - return 1; - } - - if (null === $process = $this->createServerProcess($io, $address, $documentRoot, $router)) { - return 1; - } - - $process->disableOutput(); - $process->start(); - $lockFile = $this->getLockFile($address); - touch($lockFile); - - if (!$process->isRunning()) { - $io->error('Unable to start the server process'); - unlink($lockFile); - - return 1; - } - - // stop the web server when the lock file is removed - while ($process->isRunning()) { - if (!file_exists($lockFile)) { - $process->stop(); - } - - sleep(1); - } - } - - /** - * Determine the absolute file path for the router script, using the environment to choose a standard script - * if no custom router script is specified. - * - * @param string|null $router File path of the custom router script, if set by the user; otherwise null - * @param string $env The application environment - * @param SymfonyStyle $io An SymfonyStyle instance - * - * @return string|bool The absolute file path of the router script, or false on failure - */ - private function determineRouterScript($router, $env, SymfonyStyle $io) - { - if (null === $router) { - $router = $this - ->getContainer() - ->get('kernel') - ->locateResource(sprintf('@FrameworkBundle/Resources/config/router_%s.php', $env)) - ; - } - - if (false === $path = realpath($router)) { - $io->error(sprintf('The given router script "%s" does not exist.', $router)); - - return false; - } - - return $path; - } - - /** - * Creates a process to start PHP's built-in web server. - * - * @param SymfonyStyle $io A SymfonyStyle instance - * @param string $address IP address and port to listen to - * @param string $documentRoot The application's document root - * @param string $router The router filename - * - * @return Process The process - */ - private function createServerProcess(SymfonyStyle $io, $address, $documentRoot, $router) - { - $finder = new PhpExecutableFinder(); - if (false === $binary = $finder->find()) { - $io->error('Unable to find PHP binary to start server.'); - - return; - } - - $script = implode(' ', array_map(array('Symfony\Component\Process\ProcessUtils', 'escapeArgument'), array( - $binary, - '-S', - $address, - $router, - ))); - - return new Process('exec '.$script, $documentRoot, null, null, null); - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ServerStatusCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ServerStatusCommand.php deleted file mode 100644 index d7cb9e7d08197..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ServerStatusCommand.php +++ /dev/null @@ -1,79 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Command; - -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Style\SymfonyStyle; - -/** - * Shows the status of a process that is running PHP's built-in web server in - * the background. - * - * @author Christian Flothmann - */ -class ServerStatusCommand extends ServerCommand -{ - /** - * {@inheritdoc} - */ - protected function configure() - { - $this - ->setDefinition(array( - new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1:8000'), - new InputOption('port', 'p', InputOption::VALUE_REQUIRED, 'Address port number', '8000'), - )) - ->setName('server:status') - ->setDescription('Outputs the status of the built-in web server for the given address') - ; - } - - /** - * {@inheritdoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $io = new SymfonyStyle($input, $output); - $address = $input->getArgument('address'); - - if (false === strpos($address, ':')) { - $address = $address.':'.$input->getOption('port'); - } - - // remove an orphaned lock file - if (file_exists($this->getLockFile($address)) && !$this->isServerRunning($address)) { - unlink($this->getLockFile($address)); - } - - if (file_exists($this->getLockFile($address))) { - $io->success(sprintf('Web server still listening on http://%s', $address)); - } else { - $io->warning(sprintf('No web server is listening on http://%s', $address)); - } - } - - private function isServerRunning($address) - { - list($hostname, $port) = explode(':', $address); - - if (false !== $fp = @fsockopen($hostname, $port, $errno, $errstr, 1)) { - fclose($fp); - - return true; - } - - return false; - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ServerStopCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ServerStopCommand.php deleted file mode 100644 index 8f79978a9a845..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ServerStopCommand.php +++ /dev/null @@ -1,76 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Command; - -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Style\SymfonyStyle; - -/** - * Stops a background process running PHP's built-in web server. - * - * @author Christian Flothmann - */ -class ServerStopCommand extends ServerCommand -{ - /** - * {@inheritdoc} - */ - protected function configure() - { - $this - ->setDefinition(array( - new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1'), - new InputOption('port', 'p', InputOption::VALUE_REQUIRED, 'Address port number', '8000'), - )) - ->setName('server:stop') - ->setDescription('Stops PHP\'s built-in web server that was started with the server:start command') - ->setHelp(<<<'EOF' -The %command.name% stops PHP's built-in web server: - - php %command.full_name% - -To change the default bind address and the default port use the address argument: - - php %command.full_name% 127.0.0.1:8080 - -EOF - ) - ; - } - - /** - * {@inheritdoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $io = new SymfonyStyle($input, $output); - - $address = $input->getArgument('address'); - if (false === strpos($address, ':')) { - $address = $address.':'.$input->getOption('port'); - } - - $lockFile = $this->getLockFile($address); - - if (!file_exists($lockFile)) { - $io->error(sprintf('No web server is listening on http://%s', $address)); - - return 1; - } - - unlink($lockFile); - $io->success(sprintf('Stopped the web server listening on http://%s', $address)); - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php index 299045d126b32..79b4234d81eb8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php @@ -159,7 +159,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $outputMessage .= sprintf(' and domain "%s"', $domain); } - $io->warning($outputMessage); + $io->getErrorStyle()->warning($outputMessage); return; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php index c2083e034e32c..f88747cfa61f8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php @@ -85,10 +85,11 @@ public function isEnabled() protected function execute(InputInterface $input, OutputInterface $output) { $io = new SymfonyStyle($input, $output); + $errorIo = $io->getErrorStyle(); // check presence of force or dump-message if ($input->getOption('force') !== true && $input->getOption('dump-messages') !== true) { - $io->error('You must choose one of --force or --dump-messages'); + $errorIo->error('You must choose one of --force or --dump-messages'); return 1; } @@ -97,7 +98,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $writer = $this->getContainer()->get('translation.writer'); $supportedFormats = $writer->getFormats(); if (!in_array($input->getOption('output-format'), $supportedFormats)) { - $io->error(array('Wrong output format', 'Supported formats are: '.implode(', ', $supportedFormats).'.')); + $errorIo->error(array('Wrong output format', 'Supported formats are: '.implode(', ', $supportedFormats).'.')); return 1; } @@ -127,12 +128,12 @@ protected function execute(InputInterface $input, OutputInterface $output) } } - $io->title('Translation Messages Extractor and Dumper'); - $io->comment(sprintf('Generating "%s" translation files for "%s"', $input->getArgument('locale'), $currentName)); + $errorIo->title('Translation Messages Extractor and Dumper'); + $errorIo->comment(sprintf('Generating "%s" translation files for "%s"', $input->getArgument('locale'), $currentName)); // load any messages from templates $extractedCatalogue = new MessageCatalogue($input->getArgument('locale')); - $io->comment('Parsing templates...'); + $errorIo->comment('Parsing templates...'); $extractor = $this->getContainer()->get('translation.extractor'); $extractor->setPrefix($input->getOption('no-prefix') ? '' : $input->getOption('prefix')); foreach ($transPaths as $path) { @@ -144,7 +145,7 @@ protected function execute(InputInterface $input, OutputInterface $output) // load any existing messages from the translation files $currentCatalogue = new MessageCatalogue($input->getArgument('locale')); - $io->comment('Loading translation files...'); + $errorIo->comment('Loading translation files...'); $loader = $this->getContainer()->get('translation.loader'); foreach ($transPaths as $path) { $path .= 'translations'; @@ -165,7 +166,7 @@ protected function execute(InputInterface $input, OutputInterface $output) // Exit if no messages found. if (!count($operation->getDomains())) { - $io->warning('No translation messages were found.'); + $errorIo->warning('No translation messages were found.'); return; } @@ -199,7 +200,7 @@ protected function execute(InputInterface $input, OutputInterface $output) } if ($input->getOption('output-format') == 'xlf') { - $io->comment('Xliff output version is 1.2'); + $errorIo->comment('Xliff output version is 1.2'); } $resultMessage = sprintf('%d message%s successfully extracted', $extractedMessagesCount, $extractedMessagesCount > 1 ? 's were' : ' was'); @@ -211,7 +212,7 @@ protected function execute(InputInterface $input, OutputInterface $output) // save the files if ($input->getOption('force') === true) { - $io->comment('Writing files...'); + $errorIo->comment('Writing files...'); $bundleTransPath = false; foreach ($transPaths as $path) { @@ -232,7 +233,7 @@ protected function execute(InputInterface $input, OutputInterface $output) } } - $io->success($resultMessage.'.'); + $errorIo->success($resultMessage.'.'); } private function filterCatalogue(MessageCatalogue $catalogue, $domain) diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php index 40604e3d809db..0287f42a8ed4d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php @@ -17,7 +17,6 @@ use Symfony\Component\Workflow\Dumper\GraphvizDumper; use Symfony\Component\Workflow\Dumper\StateMachineGraphvizDumper; use Symfony\Component\Workflow\Marking; -use Symfony\Component\Workflow\Workflow; /** * @author Grégoire Pineau diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/XliffLintCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/XliffLintCommand.php new file mode 100644 index 0000000000000..dcc3eb3abe2d5 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Command/XliffLintCommand.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Translation\Command\XliffLintCommand as BaseLintCommand; + +/** + * Validates XLIFF files syntax and outputs encountered errors. + * + * @author Grégoire Pineau + * @author Robin Chalas + * @author Javier Eguiluz + */ +class XliffLintCommand extends Command +{ + private $command; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this->setName('lint:xliff'); + + if (!$this->isEnabled()) { + return; + } + + $directoryIteratorProvider = function ($directory, $default) { + if (!is_dir($directory)) { + $directory = $this->getApplication()->getKernel()->locateResource($directory); + } + + return $default($directory); + }; + + $isReadableProvider = function ($fileOrDirectory, $default) { + return 0 === strpos($fileOrDirectory, '@') || $default($fileOrDirectory); + }; + + $this->command = new BaseLintCommand(null, $directoryIteratorProvider, $isReadableProvider); + + $this + ->setDescription($this->command->getDescription()) + ->setDefinition($this->command->getDefinition()) + ->setHelp($this->command->getHelp().<<<'EOF' + +Or find all files in a bundle: + + php %command.full_name% @AcmeDemoBundle + +EOF + ); + } + + /** + * {@inheritdoc} + */ + public function isEnabled() + { + return class_exists(BaseLintCommand::class); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + return $this->command->execute($input, $output); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Application.php b/src/Symfony/Bundle/FrameworkBundle/Console/Application.php index 5dfe21357f46a..da28fc0294af2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Application.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Application.php @@ -80,6 +80,16 @@ public function doRun(InputInterface $input, OutputInterface $output) return parent::doRun($input, $output); } + /** + * {@inheritdoc} + */ + public function find($name) + { + $this->registerCommands(); + + return parent::find($name); + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php index da809aea8df63..c081dfc6e96c6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php @@ -54,10 +54,10 @@ public function describe(OutputInterface $output, $object, array $options = arra $this->describeContainerTags($object, $options); break; case $object instanceof ContainerBuilder && isset($options['id']): - $this->describeContainerService($this->resolveServiceDefinition($object, $options['id']), $options); + $this->describeContainerService($this->resolveServiceDefinition($object, $options['id']), $options, $object); break; case $object instanceof ContainerBuilder && isset($options['parameter']): - $this->describeContainerParameter($object->getParameter($options['parameter']), $options); + $this->describeContainerParameter($object->resolveEnvPlaceholders($object->getParameter($options['parameter'])), $options); break; case $object instanceof ContainerBuilder: $this->describeContainerServices($object, $options); @@ -140,8 +140,9 @@ abstract protected function describeContainerTags(ContainerBuilder $builder, arr * * @param Definition|Alias|object $service * @param array $options + * @param ContainerBuilder|null $builder */ - abstract protected function describeContainerService($service, array $options = array()); + abstract protected function describeContainerService($service, array $options = array(), ContainerBuilder $builder = null); /** * Describes container services. @@ -165,10 +166,11 @@ abstract protected function describeContainerDefinition(Definition $definition, /** * Describes a service alias. * - * @param Alias $alias - * @param array $options + * @param Alias $alias + * @param array $options + * @param ContainerBuilder|null $builder */ - abstract protected function describeContainerAlias(Alias $alias, array $options = array()); + abstract protected function describeContainerAlias(Alias $alias, array $options = array(), ContainerBuilder $builder = null); /** * Describes a container parameter. @@ -309,4 +311,15 @@ protected function sortServiceIds(array $serviceIds) return $serviceIds; } + + protected function formatClosure(\Closure $closure) + { + $r = new \ReflectionFunction($closure); + + if (preg_match('#^/\*\* @closure-proxy ([^: ]++)::([^: ]++) \*/$#', $r->getDocComment(), $m)) { + return sprintf('%s::%s', $m[1], $m[2]); + } + + return 'closure'; + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php index 99adb3831b776..0b374855a72c5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php @@ -12,6 +12,8 @@ namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor; use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; @@ -77,16 +79,16 @@ protected function describeContainerTags(ContainerBuilder $builder, array $optio /** * {@inheritdoc} */ - protected function describeContainerService($service, array $options = array()) + protected function describeContainerService($service, array $options = array(), ContainerBuilder $builder = null) { if (!isset($options['id'])) { throw new \InvalidArgumentException('An "id" option must be provided.'); } if ($service instanceof Alias) { - $this->writeData($this->getContainerAliasData($service), $options); + $this->describeContainerAlias($service, $options, $builder); } elseif ($service instanceof Definition) { - $this->writeData($this->getContainerDefinitionData($service), $options); + $this->writeData($this->getContainerDefinitionData($service, isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments']), $options); } else { $this->writeData(get_class($service), $options); } @@ -99,6 +101,8 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o { $serviceIds = isset($options['tag']) && $options['tag'] ? array_keys($builder->findTaggedServiceIds($options['tag'])) : $builder->getServiceIds(); $showPrivate = isset($options['show_private']) && $options['show_private']; + $omitTags = isset($options['omit_tags']) && $options['omit_tags']; + $showArguments = isset($options['show_arguments']) && $options['show_arguments']; $data = array('definitions' => array(), 'aliases' => array(), 'services' => array()); foreach ($this->sortServiceIds($serviceIds) as $serviceId) { @@ -108,7 +112,7 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o $data['aliases'][$serviceId] = $this->getContainerAliasData($service); } elseif ($service instanceof Definition) { if (($showPrivate || $service->isPublic())) { - $data['definitions'][$serviceId] = $this->getContainerDefinitionData($service); + $data['definitions'][$serviceId] = $this->getContainerDefinitionData($service, $omitTags, $showArguments); } } else { $data['services'][$serviceId] = get_class($service); @@ -123,15 +127,22 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o */ protected function describeContainerDefinition(Definition $definition, array $options = array()) { - $this->writeData($this->getContainerDefinitionData($definition, isset($options['omit_tags']) && $options['omit_tags']), $options); + $this->writeData($this->getContainerDefinitionData($definition, isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments']), $options); } /** * {@inheritdoc} */ - protected function describeContainerAlias(Alias $alias, array $options = array()) + protected function describeContainerAlias(Alias $alias, array $options = array(), ContainerBuilder $builder = null) { - $this->writeData($this->getContainerAliasData($alias), $options); + if (!$builder) { + return $this->writeData($this->getContainerAliasData($alias), $options); + } + + $this->writeData( + array($this->getContainerAliasData($alias), $this->getContainerDefinitionData($builder->getDefinition((string) $alias), isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments'])), + array_merge($options, array('id' => (string) $alias)) + ); } /** @@ -201,28 +212,25 @@ protected function getRouteData(Route $route) * * @return array */ - private function getContainerDefinitionData(Definition $definition, $omitTags = false) + private function getContainerDefinitionData(Definition $definition, $omitTags = false, $showArguments = false) { $data = array( 'class' => (string) $definition->getClass(), 'public' => $definition->isPublic(), 'synthetic' => $definition->isSynthetic(), 'lazy' => $definition->isLazy(), + 'shared' => $definition->isShared(), + 'abstract' => $definition->isAbstract(), + 'autowire' => $definition->isAutowired(), + 'autoconfigure' => $definition->isAutoconfigured(), ); - if (method_exists($definition, 'isShared')) { - $data['shared'] = $definition->isShared(); + foreach ($definition->getAutowiringTypes(false) as $autowiringType) { + $data['autowiring_types'][] = $autowiringType; } - $data['abstract'] = $definition->isAbstract(); - - if (method_exists($definition, 'isAutowired')) { - $data['autowire'] = $definition->isAutowired(); - - $data['autowiring_types'] = array(); - foreach ($definition->getAutowiringTypes() as $autowiringType) { - $data['autowiring_types'][] = $autowiringType; - } + if ($showArguments) { + $data['arguments'] = $this->describeValue($definition->getArguments(), $omitTags, $showArguments); } $data['file'] = $definition->getFile(); @@ -252,11 +260,9 @@ private function getContainerDefinitionData(Definition $definition, $omitTags = if (!$omitTags) { $data['tags'] = array(); - if (count($definition->getTags())) { - foreach ($definition->getTags() as $tagName => $tagData) { - foreach ($tagData as $parameters) { - $data['tags'][] = array('name' => $tagName, 'parameters' => $parameters); - } + foreach ($definition->getTags() as $tagName => $tagData) { + foreach ($tagData as $parameters) { + $data['tags'][] = array('name' => $tagName, 'parameters' => $parameters); } } } @@ -358,7 +364,7 @@ private function getCallableData($callable, array $options = array()) } if ($callable instanceof \Closure) { - $data['type'] = 'closure'; + $data['type'] = $this->formatClosure($callable); return $data; } @@ -372,4 +378,37 @@ private function getCallableData($callable, array $options = array()) throw new \InvalidArgumentException('Callable is not describable.'); } + + private function describeValue($value, $omitTags, $showArguments) + { + if (is_array($value)) { + $data = array(); + foreach ($value as $k => $v) { + $data[$k] = $this->describeValue($v, $omitTags, $showArguments); + } + + return $data; + } + + if ($value instanceof ServiceClosureArgument) { + $value = $value->getValues()[0]; + } + + if ($value instanceof Reference) { + return array( + 'type' => 'service', + 'id' => (string) $value, + ); + } + + if ($value instanceof ArgumentInterface) { + return $this->describeValue($value->getValues(), $omitTags, $showArguments); + } + + if ($value instanceof Definition) { + return $this->getContainerDefinitionData($value, $omitTags, $showArguments); + } + + return $value; + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php index 5c91fa3bf9ad2..d44d577950729 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php @@ -97,16 +97,16 @@ protected function describeContainerTags(ContainerBuilder $builder, array $optio /** * {@inheritdoc} */ - protected function describeContainerService($service, array $options = array()) + protected function describeContainerService($service, array $options = array(), ContainerBuilder $builder = null) { if (!isset($options['id'])) { throw new \InvalidArgumentException('An "id" option must be provided.'); } - $childOptions = array('id' => $options['id'], 'as_array' => true); + $childOptions = array_merge($options, array('id' => $options['id'], 'as_array' => true)); if ($service instanceof Alias) { - $this->describeContainerAlias($service, $childOptions); + $this->describeContainerAlias($service, $childOptions, $builder); } elseif ($service instanceof Definition) { $this->describeContainerDefinition($service, $childOptions); } else { @@ -129,6 +129,7 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o $serviceIds = isset($options['tag']) && $options['tag'] ? array_keys($builder->findTaggedServiceIds($options['tag'])) : $builder->getServiceIds(); $showPrivate = isset($options['show_private']) && $options['show_private']; + $showArguments = isset($options['show_arguments']) && $options['show_arguments']; $services = array('definitions' => array(), 'aliases' => array(), 'services' => array()); foreach ($this->sortServiceIds($serviceIds) as $serviceId) { @@ -149,7 +150,7 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o $this->write("\n\nDefinitions\n-----------\n"); foreach ($services['definitions'] as $id => $service) { $this->write("\n"); - $this->describeContainerDefinition($service, array('id' => $id)); + $this->describeContainerDefinition($service, array('id' => $id, 'show_arguments' => $showArguments)); } } @@ -179,20 +180,18 @@ protected function describeContainerDefinition(Definition $definition, array $op ."\n".'- Public: '.($definition->isPublic() ? 'yes' : 'no') ."\n".'- Synthetic: '.($definition->isSynthetic() ? 'yes' : 'no') ."\n".'- Lazy: '.($definition->isLazy() ? 'yes' : 'no') + ."\n".'- Shared: '.($definition->isShared() ? 'yes' : 'no') + ."\n".'- Abstract: '.($definition->isAbstract() ? 'yes' : 'no') + ."\n".'- Autowired: '.($definition->isAutowired() ? 'yes' : 'no') + ."\n".'- Autoconfigured: '.($definition->isAutoconfigured() ? 'yes' : 'no') ; - if (method_exists($definition, 'isShared')) { - $output .= "\n".'- Shared: '.($definition->isShared() ? 'yes' : 'no'); + foreach ($definition->getAutowiringTypes(false) as $autowiringType) { + $output .= "\n".'- Autowiring Type: `'.$autowiringType.'`'; } - $output .= "\n".'- Abstract: '.($definition->isAbstract() ? 'yes' : 'no'); - - if (method_exists($definition, 'isAutowired')) { - $output .= "\n".'- Autowired: '.($definition->isAutowired() ? 'yes' : 'no'); - - foreach ($definition->getAutowiringTypes() as $autowiringType) { - $output .= "\n".'- Autowiring Type: `'.$autowiringType.'`'; - } + if (isset($options['show_arguments']) && $options['show_arguments']) { + $output .= "\n".'- Arguments: '.($definition->getArguments() ? 'yes' : 'no'); } if ($definition->getFile()) { @@ -230,18 +229,29 @@ protected function describeContainerDefinition(Definition $definition, array $op } } - $this->write(isset($options['id']) ? sprintf("%s\n%s\n\n%s\n", $options['id'], str_repeat('~', strlen($options['id'])), $output) : $output); + $this->write(isset($options['id']) ? sprintf("### %s\n\n%s\n", $options['id'], $output) : $output); } /** * {@inheritdoc} */ - protected function describeContainerAlias(Alias $alias, array $options = array()) + protected function describeContainerAlias(Alias $alias, array $options = array(), ContainerBuilder $builder = null) { $output = '- Service: `'.$alias.'`' ."\n".'- Public: '.($alias->isPublic() ? 'yes' : 'no'); - $this->write(isset($options['id']) ? sprintf("%s\n%s\n\n%s\n", $options['id'], str_repeat('~', strlen($options['id'])), $output) : $output); + if (!isset($options['id'])) { + return $this->write($output); + } + + $this->write(sprintf("### %s\n\n%s\n", $options['id'], $output)); + + if (!$builder) { + return; + } + + $this->write("\n"); + $this->describeContainerDefinition($builder->getDefinition((string) $alias), array_merge($options, array('id' => (string) $alias))); } /** @@ -334,7 +344,8 @@ protected function describeCallable($callable, array $options = array()) } if ($callable instanceof \Closure) { - $string .= "\n- Type: `closure`"; + $formatted = $this->formatClosure($callable); + $string .= "\n- Type: `$formatted`"; return $this->write($string."\n"); } @@ -356,7 +367,7 @@ protected function describeCallable($callable, array $options = array()) */ private function formatRouterConfig(array $array) { - if (!count($array)) { + if (!$array) { return 'NONE'; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php index 7d876eb71336a..7aae3d3c198bb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php @@ -14,6 +14,9 @@ use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; @@ -92,6 +95,9 @@ protected function describeRoute(Route $route, array $options = array()) array('Defaults', $this->formatRouterConfig($route->getDefaults())), array('Options', $this->formatRouterConfig($route->getOptions())), ); + if (isset($options['callable'])) { + $tableRows[] = array('Callable', $options['callable']); + } $table = new Table($this->getOutput()); $table->setHeaders($tableHeaders)->setRows($tableRows); @@ -136,14 +142,14 @@ protected function describeContainerTags(ContainerBuilder $builder, array $optio /** * {@inheritdoc} */ - protected function describeContainerService($service, array $options = array()) + protected function describeContainerService($service, array $options = array(), ContainerBuilder $builder = null) { if (!isset($options['id'])) { throw new \InvalidArgumentException('An "id" option must be provided.'); } if ($service instanceof Alias) { - $this->describeContainerAlias($service, $options); + $this->describeContainerAlias($service, $options, $builder); } elseif ($service instanceof Definition) { $this->describeContainerDefinition($service, $options); } else { @@ -252,9 +258,9 @@ protected function describeContainerDefinition(Definition $definition, array $op $tableRows[] = array('Service ID', isset($options['id']) ? $options['id'] : '-'); $tableRows[] = array('Class', $definition->getClass() ?: '-'); - $tags = $definition->getTags(); - if (count($tags)) { - $tagInformation = ''; + $omitTags = isset($options['omit_tags']) && $options['omit_tags']; + if (!$omitTags && ($tags = $definition->getTags())) { + $tagInformation = array(); foreach ($tags as $tagName => $tagData) { foreach ($tagData as $tagParameters) { $parameters = array_map(function ($key, $value) { @@ -263,12 +269,13 @@ protected function describeContainerDefinition(Definition $definition, array $op $parameters = implode(', ', $parameters); if ('' === $parameters) { - $tagInformation .= sprintf('%s', $tagName); + $tagInformation[] = sprintf('%s', $tagName); } else { - $tagInformation .= sprintf('%s (%s)', $tagName, $parameters); + $tagInformation[] = sprintf('%s (%s)', $tagName, $parameters); } } } + $tagInformation = implode("\n", $tagInformation); } else { $tagInformation = '-'; } @@ -286,22 +293,12 @@ protected function describeContainerDefinition(Definition $definition, array $op $tableRows[] = array('Public', $definition->isPublic() ? 'yes' : 'no'); $tableRows[] = array('Synthetic', $definition->isSynthetic() ? 'yes' : 'no'); $tableRows[] = array('Lazy', $definition->isLazy() ? 'yes' : 'no'); - if (method_exists($definition, 'isShared')) { - $tableRows[] = array('Shared', $definition->isShared() ? 'yes' : 'no'); - } + $tableRows[] = array('Shared', $definition->isShared() ? 'yes' : 'no'); $tableRows[] = array('Abstract', $definition->isAbstract() ? 'yes' : 'no'); + $tableRows[] = array('Autowired', $definition->isAutowired() ? 'yes' : 'no'); - if (method_exists($definition, 'isAutowired')) { - $tableRows[] = array('Autowired', $definition->isAutowired() ? 'yes' : 'no'); - - $autowiringTypes = $definition->getAutowiringTypes(); - if (count($autowiringTypes)) { - $autowiringTypesInformation = implode(', ', $autowiringTypes); - } else { - $autowiringTypesInformation = '-'; - } - - $tableRows[] = array('Autowiring Types', $autowiringTypesInformation); + if ($autowiringTypes = $definition->getAutowiringTypes(false)) { + $tableRows[] = array('Autowiring Types', implode(', ', $autowiringTypes)); } if ($definition->getFile()) { @@ -323,15 +320,45 @@ protected function describeContainerDefinition(Definition $definition, array $op } } + $showArguments = isset($options['show_arguments']) && $options['show_arguments']; + $argumentsInformation = array(); + if ($showArguments && ($arguments = $definition->getArguments())) { + foreach ($arguments as $argument) { + if ($argument instanceof ServiceClosureArgument) { + $argument = $argument->getValues()[0]; + } + if ($argument instanceof Reference) { + $argumentsInformation[] = sprintf('Service(%s)', (string) $argument); + } elseif ($argument instanceof IteratorArgument) { + $argumentsInformation[] = sprintf('Iterator (%d element(s))', count($argument->getValues())); + } elseif ($argument instanceof ClosureProxyArgument) { + list($reference, $method) = $argument->getValues(); + $argumentsInformation[] = sprintf('ClosureProxy(Service(%s)::%s())', $reference, $method); + } elseif ($argument instanceof Definition) { + $argumentsInformation[] = 'Inlined Service'; + } else { + $argumentsInformation[] = is_array($argument) ? sprintf('Array (%d element(s))', count($argument)) : $argument; + } + } + + $tableRows[] = array('Arguments', implode("\n", $argumentsInformation)); + } + $options['output']->table($tableHeaders, $tableRows); } /** * {@inheritdoc} */ - protected function describeContainerAlias(Alias $alias, array $options = array()) + protected function describeContainerAlias(Alias $alias, array $options = array(), ContainerBuilder $builder = null) { $options['output']->comment(sprintf('This service is an alias for the service %s', (string) $alias)); + + if (!$builder) { + return; + } + + return $this->describeContainerDefinition($builder->getDefinition((string) $alias), array_merge($options, array('id' => (string) $alias))); } /** @@ -439,7 +466,13 @@ private function formatCallable($callable) } if ($callable instanceof \Closure) { - return '\Closure()'; + $formatted = $this->formatClosure($callable); + + if ('closure' === $formatted) { + return '\Closure()'; + } + + return $formatted.'()'; } if (method_exists($callable, '__invoke')) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php index 7d7d5f0956ed4..461a4e086f9cd 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php @@ -12,6 +12,9 @@ namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor; use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; @@ -62,13 +65,13 @@ protected function describeContainerTags(ContainerBuilder $builder, array $optio /** * {@inheritdoc} */ - protected function describeContainerService($service, array $options = array()) + protected function describeContainerService($service, array $options = array(), ContainerBuilder $builder = null) { if (!isset($options['id'])) { throw new \InvalidArgumentException('An "id" option must be provided.'); } - $this->writeDocument($this->getContainerServiceDocument($service, $options['id'])); + $this->writeDocument($this->getContainerServiceDocument($service, $options['id'], $builder, isset($options['show_arguments']) && $options['show_arguments'])); } /** @@ -76,7 +79,7 @@ protected function describeContainerService($service, array $options = array()) */ protected function describeContainerServices(ContainerBuilder $builder, array $options = array()) { - $this->writeDocument($this->getContainerServicesDocument($builder, isset($options['tag']) ? $options['tag'] : null, isset($options['show_private']) && $options['show_private'])); + $this->writeDocument($this->getContainerServicesDocument($builder, isset($options['tag']) ? $options['tag'] : null, isset($options['show_private']) && $options['show_private'], isset($options['show_arguments']) && $options['show_arguments'])); } /** @@ -84,15 +87,24 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o */ protected function describeContainerDefinition(Definition $definition, array $options = array()) { - $this->writeDocument($this->getContainerDefinitionDocument($definition, isset($options['id']) ? $options['id'] : null, isset($options['omit_tags']) && $options['omit_tags'])); + $this->writeDocument($this->getContainerDefinitionDocument($definition, isset($options['id']) ? $options['id'] : null, isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments'])); } /** * {@inheritdoc} */ - protected function describeContainerAlias(Alias $alias, array $options = array()) + protected function describeContainerAlias(Alias $alias, array $options = array(), ContainerBuilder $builder = null) { - $this->writeDocument($this->getContainerAliasDocument($alias, isset($options['id']) ? $options['id'] : null)); + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($dom->importNode($this->getContainerAliasDocument($alias, isset($options['id']) ? $options['id'] : null)->childNodes->item(0), true)); + + if (!$builder) { + return $this->writeDocument($dom); + } + + $dom->appendChild($dom->importNode($this->getContainerDefinitionDocument($builder->getDefinition((string) $alias), (string) $alias)->childNodes->item(0), true)); + + $this->writeDocument($dom); } /** @@ -187,7 +199,7 @@ private function getRouteDocument(Route $route, $name = null) $methodXML->appendChild(new \DOMText($method)); } - if (count($route->getDefaults())) { + if ($route->getDefaults()) { $routeXML->appendChild($defaultsXML = $dom->createElement('defaults')); foreach ($route->getDefaults() as $attribute => $value) { $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); @@ -196,16 +208,18 @@ private function getRouteDocument(Route $route, $name = null) } } - if (count($route->getRequirements())) { + $originRequirements = $requirements = $route->getRequirements(); + unset($requirements['_scheme'], $requirements['_method']); + if ($requirements) { $routeXML->appendChild($requirementsXML = $dom->createElement('requirements')); - foreach ($route->getRequirements() as $attribute => $pattern) { + foreach ($originRequirements as $attribute => $pattern) { $requirementsXML->appendChild($requirementXML = $dom->createElement('requirement')); $requirementXML->setAttribute('key', $attribute); $requirementXML->appendChild(new \DOMText($pattern)); } } - if (count($route->getOptions())) { + if ($route->getOptions()) { $routeXML->appendChild($optionsXML = $dom->createElement('options')); foreach ($route->getOptions() as $name => $value) { $optionsXML->appendChild($optionXML = $dom->createElement('option')); @@ -261,19 +275,24 @@ private function getContainerTagsDocument(ContainerBuilder $builder, $showPrivat } /** - * @param mixed $service - * @param string $id + * @param mixed $service + * @param string $id + * @param ContainerBuilder|null $builder + * @param bool $showArguments * * @return \DOMDocument */ - private function getContainerServiceDocument($service, $id) + private function getContainerServiceDocument($service, $id, ContainerBuilder $builder = null, $showArguments = false) { $dom = new \DOMDocument('1.0', 'UTF-8'); if ($service instanceof Alias) { $dom->appendChild($dom->importNode($this->getContainerAliasDocument($service, $id)->childNodes->item(0), true)); + if ($builder) { + $dom->appendChild($dom->importNode($this->getContainerDefinitionDocument($builder->getDefinition((string) $service), (string) $service, false, $showArguments)->childNodes->item(0), true)); + } } elseif ($service instanceof Definition) { - $dom->appendChild($dom->importNode($this->getContainerDefinitionDocument($service, $id)->childNodes->item(0), true)); + $dom->appendChild($dom->importNode($this->getContainerDefinitionDocument($service, $id, false, $showArguments)->childNodes->item(0), true)); } else { $dom->appendChild($serviceXML = $dom->createElement('service')); $serviceXML->setAttribute('id', $id); @@ -287,10 +306,11 @@ private function getContainerServiceDocument($service, $id) * @param ContainerBuilder $builder * @param string|null $tag * @param bool $showPrivate + * @param bool $showArguments * * @return \DOMDocument */ - private function getContainerServicesDocument(ContainerBuilder $builder, $tag = null, $showPrivate = false) + private function getContainerServicesDocument(ContainerBuilder $builder, $tag = null, $showPrivate = false, $showArguments = false) { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($containerXML = $dom->createElement('container')); @@ -304,7 +324,7 @@ private function getContainerServicesDocument(ContainerBuilder $builder, $tag = continue; } - $serviceXML = $this->getContainerServiceDocument($service, $serviceId); + $serviceXML = $this->getContainerServiceDocument($service, $serviceId, null, $showArguments); $containerXML->appendChild($containerXML->ownerDocument->importNode($serviceXML->childNodes->item(0), true)); } @@ -318,7 +338,7 @@ private function getContainerServicesDocument(ContainerBuilder $builder, $tag = * * @return \DOMDocument */ - private function getContainerDefinitionDocument(Definition $definition, $id = null, $omitTags = false) + private function getContainerDefinitionDocument(Definition $definition, $id = null, $omitTags = false, $showArguments = false) { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($serviceXML = $dom->createElement('definition')); @@ -349,15 +369,10 @@ private function getContainerDefinitionDocument(Definition $definition, $id = nu $serviceXML->setAttribute('public', $definition->isPublic() ? 'true' : 'false'); $serviceXML->setAttribute('synthetic', $definition->isSynthetic() ? 'true' : 'false'); $serviceXML->setAttribute('lazy', $definition->isLazy() ? 'true' : 'false'); - if (method_exists($definition, 'isShared')) { - $serviceXML->setAttribute('shared', $definition->isShared() ? 'true' : 'false'); - } + $serviceXML->setAttribute('shared', $definition->isShared() ? 'true' : 'false'); $serviceXML->setAttribute('abstract', $definition->isAbstract() ? 'true' : 'false'); - - if (method_exists($definition, 'isAutowired')) { - $serviceXML->setAttribute('autowired', $definition->isAutowired() ? 'true' : 'false'); - } - + $serviceXML->setAttribute('autowired', $definition->isAutowired() ? 'true' : 'false'); + $serviceXML->setAttribute('autoconfigured', $definition->isAutoconfigured() ? 'true' : 'false'); $serviceXML->setAttribute('file', $definition->getFile()); $calls = $definition->getMethodCalls(); @@ -369,10 +384,14 @@ private function getContainerDefinitionDocument(Definition $definition, $id = nu } } - if (!$omitTags) { - $tags = $definition->getTags(); + if ($showArguments) { + foreach ($this->getArgumentNodes($definition->getArguments(), $dom) as $node) { + $serviceXML->appendChild($node); + } + } - if (count($tags) > 0) { + if (!$omitTags) { + if ($tags = $definition->getTags()) { $serviceXML->appendChild($tagsXML = $dom->createElement('tags')); foreach ($tags as $tagName => $tagData) { foreach ($tagData as $parameters) { @@ -391,6 +410,58 @@ private function getContainerDefinitionDocument(Definition $definition, $id = nu return $dom; } + /** + * @param array $arguments + * + * @return \DOMNode[] + */ + private function getArgumentNodes(array $arguments, \DOMDocument $dom) + { + $nodes = array(); + + foreach ($arguments as $argumentKey => $argument) { + $argumentXML = $dom->createElement('argument'); + + if (is_string($argumentKey)) { + $argumentXML->setAttribute('key', $argumentKey); + } + + if ($argument instanceof ServiceClosureArgument) { + $argument = $argument->getValues()[0]; + } + + if ($argument instanceof Reference) { + $argumentXML->setAttribute('type', 'service'); + $argumentXML->setAttribute('id', (string) $argument); + } elseif ($argument instanceof IteratorArgument) { + $argumentXML->setAttribute('type', 'iterator'); + + foreach ($this->getArgumentNodes($argument->getValues(), $dom) as $childArgumentXML) { + $argumentXML->appendChild($childArgumentXML); + } + } elseif ($argument instanceof ClosureProxyArgument) { + list($reference, $method) = $argument->getValues(); + $argumentXML->setAttribute('type', 'closure-proxy'); + $argumentXML->setAttribute('id', (string) $reference); + $argumentXML->setAttribute('method', $method); + } elseif ($argument instanceof Definition) { + $argumentXML->appendChild($dom->importNode($this->getContainerDefinitionDocument($argument, null, false, true)->childNodes->item(0), true)); + } elseif (is_array($argument)) { + $argumentXML->setAttribute('type', 'collection'); + + foreach ($this->getArgumentNodes($argument, $dom) as $childArgumenXML) { + $argumentXML->appendChild($childArgumenXML); + } + } else { + $argumentXML->appendChild(new \DOMText($argument)); + } + + $nodes[] = $argumentXML; + } + + return $nodes; + } + /** * @param Alias $alias * @param string|null $id @@ -523,7 +594,7 @@ private function getCallableDocument($callable) } if ($callable instanceof \Closure) { - $callableXML->setAttribute('type', 'closure'); + $callableXML->setAttribute('type', $this->formatClosure($callable)); return $dom; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php b/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php new file mode 100644 index 0000000000000..1055b00e3b1b2 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Controller; + +use Psr\Container\ContainerInterface; +use Doctrine\Common\Persistence\ManagerRegistry; +use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Component\Form\FormFactoryInterface; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\SessionInterface; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\Routing\RouterInterface; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; +use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; +use Symfony\Component\Serializer\SerializerInterface; +use Symfony\Component\Templating\EngineInterface; + +/** + * Provides common features needed in controllers. + * + * @author Fabien Potencier + */ +abstract class AbstractController implements ServiceSubscriberInterface +{ + use ControllerTrait; + + private $container; + + /** + * @internal + * @required + */ + public function setContainer(ContainerInterface $container) + { + $previous = $this->container; + $this->container = $container; + + return $previous; + } + + public static function getSubscribedServices() + { + return array( + 'router' => '?'.RouterInterface::class, + 'request_stack' => '?'.RequestStack::class, + 'http_kernel' => '?'.HttpKernelInterface::class, + 'serializer' => '?'.SerializerInterface::class, + 'session' => '?'.SessionInterface::class, + 'security.authorization_checker' => '?'.AuthorizationCheckerInterface::class, + 'templating' => '?'.EngineInterface::class, + 'twig' => '?'.\Twig_Environment::class, + 'doctrine' => '?'.ManagerRegistry::class, + 'form.factory' => '?'.FormFactoryInterface::class, + 'security.token_storage' => '?'.TokenStorageInterface::class, + 'security.csrf.token_manager' => '?'.CsrfTokenManagerInterface::class, + ); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php b/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php index 1dbf54b5adf9d..870965201e397 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php @@ -13,21 +13,6 @@ use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerAwareTrait; -use Symfony\Component\HttpFoundation\BinaryFileResponse; -use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpFoundation\RedirectResponse; -use Symfony\Component\HttpFoundation\ResponseHeaderBag; -use Symfony\Component\HttpFoundation\StreamedResponse; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Symfony\Component\HttpKernel\HttpKernelInterface; -use Symfony\Component\Security\Core\Exception\AccessDeniedException; -use Symfony\Component\Security\Csrf\CsrfToken; -use Symfony\Component\Form\Extension\Core\Type\FormType; -use Symfony\Component\Form\Form; -use Symfony\Component\Form\FormBuilder; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Doctrine\Bundle\DoctrineBundle\Registry; /** * Controller is a simple implementation of a Controller. @@ -39,354 +24,7 @@ abstract class Controller implements ContainerAwareInterface { use ContainerAwareTrait; - - /** - * Generates a URL from the given parameters. - * - * @param string $route The name of the route - * @param mixed $parameters An array of parameters - * @param int $referenceType The type of reference (one of the constants in UrlGeneratorInterface) - * - * @return string The generated URL - * - * @see UrlGeneratorInterface - */ - protected function generateUrl($route, $parameters = array(), $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH) - { - return $this->container->get('router')->generate($route, $parameters, $referenceType); - } - - /** - * Forwards the request to another controller. - * - * @param string $controller The controller name (a string like BlogBundle:Post:index) - * @param array $path An array of path parameters - * @param array $query An array of query parameters - * - * @return Response A Response instance - */ - protected function forward($controller, array $path = array(), array $query = array()) - { - $request = $this->container->get('request_stack')->getCurrentRequest(); - $path['_forwarded'] = $request->attributes; - $path['_controller'] = $controller; - $subRequest = $request->duplicate($query, null, $path); - - return $this->container->get('http_kernel')->handle($subRequest, HttpKernelInterface::SUB_REQUEST); - } - - /** - * Returns a RedirectResponse to the given URL. - * - * @param string $url The URL to redirect to - * @param int $status The status code to use for the Response - * - * @return RedirectResponse - */ - protected function redirect($url, $status = 302) - { - return new RedirectResponse($url, $status); - } - - /** - * Returns a RedirectResponse to the given route with the given parameters. - * - * @param string $route The name of the route - * @param array $parameters An array of parameters - * @param int $status The status code to use for the Response - * - * @return RedirectResponse - */ - protected function redirectToRoute($route, array $parameters = array(), $status = 302) - { - return $this->redirect($this->generateUrl($route, $parameters), $status); - } - - /** - * Returns a JsonResponse that uses the serializer component if enabled, or json_encode. - * - * @param mixed $data The response data - * @param int $status The status code to use for the Response - * @param array $headers Array of extra headers to add - * @param array $context Context to pass to serializer when using serializer component - * - * @return JsonResponse - */ - protected function json($data, $status = 200, $headers = array(), $context = array()) - { - if ($this->container->has('serializer')) { - $json = $this->container->get('serializer')->serialize($data, 'json', array_merge(array( - 'json_encode_options' => JsonResponse::DEFAULT_ENCODING_OPTIONS, - ), $context)); - - return new JsonResponse($json, $status, $headers, true); - } - - return new JsonResponse($data, $status, $headers); - } - - /** - * Returns a BinaryFileResponse object with original or customized file name and disposition header. - * - * @param \SplFileInfo|string $file File object or path to file to be sent as response - * @param string|null $fileName File name to be sent to response or null (will use original file name) - * @param string $disposition Disposition of response ("attachment" is default, other type is "inline") - * - * @return BinaryFileResponse - */ - protected function file($file, $fileName = null, $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT) - { - $response = new BinaryFileResponse($file); - $response->setContentDisposition($disposition, $fileName === null ? $response->getFile()->getFileName() : $fileName); - - return $response; - } - - /** - * Adds a flash message to the current session for type. - * - * @param string $type The type - * @param string $message The message - * - * @throws \LogicException - */ - protected function addFlash($type, $message) - { - if (!$this->container->has('session')) { - throw new \LogicException('You can not use the addFlash method if sessions are disabled.'); - } - - $this->container->get('session')->getFlashBag()->add($type, $message); - } - - /** - * Checks if the attributes are granted against the current authentication token and optionally supplied object. - * - * @param mixed $attributes The attributes - * @param mixed $object The object - * - * @return bool - * - * @throws \LogicException - */ - protected function isGranted($attributes, $object = null) - { - if (!$this->container->has('security.authorization_checker')) { - throw new \LogicException('The SecurityBundle is not registered in your application.'); - } - - return $this->container->get('security.authorization_checker')->isGranted($attributes, $object); - } - - /** - * Throws an exception unless the attributes are granted against the current authentication token and optionally - * supplied object. - * - * @param mixed $attributes The attributes - * @param mixed $object The object - * @param string $message The message passed to the exception - * - * @throws AccessDeniedException - */ - protected function denyAccessUnlessGranted($attributes, $object = null, $message = 'Access Denied.') - { - if (!$this->isGranted($attributes, $object)) { - $exception = $this->createAccessDeniedException($message); - $exception->setAttributes($attributes); - $exception->setSubject($object); - - throw $exception; - } - } - - /** - * Returns a rendered view. - * - * @param string $view The view name - * @param array $parameters An array of parameters to pass to the view - * - * @return string The rendered view - */ - protected function renderView($view, array $parameters = array()) - { - if ($this->container->has('templating')) { - return $this->container->get('templating')->render($view, $parameters); - } - - if (!$this->container->has('twig')) { - throw new \LogicException('You can not use the "renderView" method if the Templating Component or the Twig Bundle are not available.'); - } - - return $this->container->get('twig')->render($view, $parameters); - } - - /** - * Renders a view. - * - * @param string $view The view name - * @param array $parameters An array of parameters to pass to the view - * @param Response $response A response instance - * - * @return Response A Response instance - */ - protected function render($view, array $parameters = array(), Response $response = null) - { - if ($this->container->has('templating')) { - return $this->container->get('templating')->renderResponse($view, $parameters, $response); - } - - if (!$this->container->has('twig')) { - throw new \LogicException('You can not use the "render" method if the Templating Component or the Twig Bundle are not available.'); - } - - if (null === $response) { - $response = new Response(); - } - - $response->setContent($this->container->get('twig')->render($view, $parameters)); - - return $response; - } - - /** - * Streams a view. - * - * @param string $view The view name - * @param array $parameters An array of parameters to pass to the view - * @param StreamedResponse $response A response instance - * - * @return StreamedResponse A StreamedResponse instance - */ - protected function stream($view, array $parameters = array(), StreamedResponse $response = null) - { - if ($this->container->has('templating')) { - $templating = $this->container->get('templating'); - - $callback = function () use ($templating, $view, $parameters) { - $templating->stream($view, $parameters); - }; - } elseif ($this->container->has('twig')) { - $twig = $this->container->get('twig'); - - $callback = function () use ($twig, $view, $parameters) { - $twig->display($view, $parameters); - }; - } else { - throw new \LogicException('You can not use the "stream" method if the Templating Component or the Twig Bundle are not available.'); - } - - if (null === $response) { - return new StreamedResponse($callback); - } - - $response->setCallback($callback); - - return $response; - } - - /** - * Returns a NotFoundHttpException. - * - * This will result in a 404 response code. Usage example: - * - * throw $this->createNotFoundException('Page not found!'); - * - * @param string $message A message - * @param \Exception|null $previous The previous exception - * - * @return NotFoundHttpException - */ - protected function createNotFoundException($message = 'Not Found', \Exception $previous = null) - { - return new NotFoundHttpException($message, $previous); - } - - /** - * Returns an AccessDeniedException. - * - * This will result in a 403 response code. Usage example: - * - * throw $this->createAccessDeniedException('Unable to access this page!'); - * - * @param string $message A message - * @param \Exception|null $previous The previous exception - * - * @return AccessDeniedException - */ - protected function createAccessDeniedException($message = 'Access Denied.', \Exception $previous = null) - { - return new AccessDeniedException($message, $previous); - } - - /** - * Creates and returns a Form instance from the type of the form. - * - * @param string $type The fully qualified class name of the form type - * @param mixed $data The initial data for the form - * @param array $options Options for the form - * - * @return Form - */ - protected function createForm($type, $data = null, array $options = array()) - { - return $this->container->get('form.factory')->create($type, $data, $options); - } - - /** - * Creates and returns a form builder instance. - * - * @param mixed $data The initial data for the form - * @param array $options Options for the form - * - * @return FormBuilder - */ - protected function createFormBuilder($data = null, array $options = array()) - { - return $this->container->get('form.factory')->createBuilder(FormType::class, $data, $options); - } - - /** - * Shortcut to return the Doctrine Registry service. - * - * @return Registry - * - * @throws \LogicException If DoctrineBundle is not available - */ - protected function getDoctrine() - { - if (!$this->container->has('doctrine')) { - throw new \LogicException('The DoctrineBundle is not registered in your application.'); - } - - return $this->container->get('doctrine'); - } - - /** - * Get a user from the Security Token Storage. - * - * @return mixed - * - * @throws \LogicException If SecurityBundle is not available - * - * @see TokenInterface::getUser() - */ - protected function getUser() - { - if (!$this->container->has('security.token_storage')) { - throw new \LogicException('The SecurityBundle is not registered in your application.'); - } - - if (null === $token = $this->container->get('security.token_storage')->getToken()) { - return; - } - - if (!is_object($user = $token->getUser())) { - // e.g. anonymous authentication - return; - } - - return $user; - } + use ControllerTrait; /** * Returns true if the service id is defined. @@ -423,21 +61,4 @@ protected function getParameter($name) { return $this->container->getParameter($name); } - - /** - * Checks the validity of a CSRF token. - * - * @param string $id The id used when generating the token - * @param string $token The actual token sent with the request that should be validated - * - * @return bool - */ - protected function isCsrfTokenValid($id, $token) - { - if (!$this->container->has('security.csrf.token_manager')) { - throw new \LogicException('CSRF protection is not enabled in your application.'); - } - - return $this->container->get('security.csrf.token_manager')->isTokenValid(new CsrfToken($id, $token)); - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php index 31593b9b80506..355f526fdc950 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php @@ -12,18 +12,17 @@ namespace Symfony\Bundle\FrameworkBundle\Controller; use Psr\Log\LoggerInterface; -use Symfony\Component\HttpKernel\Controller\ControllerResolver as BaseControllerResolver; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\HttpKernel\Controller\ContainerControllerResolver; /** * ControllerResolver. * * @author Fabien Potencier */ -class ControllerResolver extends BaseControllerResolver +class ControllerResolver extends ContainerControllerResolver { - protected $container; protected $parser; /** @@ -35,39 +34,19 @@ class ControllerResolver extends BaseControllerResolver */ public function __construct(ContainerInterface $container, ControllerNameParser $parser, LoggerInterface $logger = null) { - $this->container = $container; $this->parser = $parser; - parent::__construct($logger); + parent::__construct($container, $logger); } /** - * Returns a callable for the given controller. - * - * @param string $controller A Controller string - * - * @return mixed A PHP callable - * - * @throws \LogicException When the name could not be parsed - * @throws \InvalidArgumentException When the controller class does not exist + * {@inheritdoc} */ protected function createController($controller) { - if (false === strpos($controller, '::')) { - $count = substr_count($controller, ':'); - if (2 == $count) { - // controller in the a:b:c notation then - $controller = $this->parser->parse($controller); - } elseif (1 == $count) { - // controller in the service:method notation - list($service, $method) = explode(':', $controller, 2); - - return array($this->container->get($service), $method); - } elseif ($this->container->has($controller) && method_exists($service = $this->container->get($controller), '__invoke')) { - return $service; - } else { - throw new \LogicException(sprintf('Unable to parse the controller name "%s".', $controller)); - } + if (false === strpos($controller, '::') && 2 === substr_count($controller, ':')) { + // controller in the a:b:c notation then + $controller = $this->parser->parse($controller); } return parent::createController($controller); @@ -78,15 +57,14 @@ 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) { $controller->setContainer($this->container); } + if ($controller instanceof AbstractController && null !== $previousContainer = $controller->setContainer($this->container)) { + $controller->setContainer($previousContainer); + } return $controller; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php new file mode 100644 index 0000000000000..f138186c34d07 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php @@ -0,0 +1,403 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Controller; + +use Symfony\Component\HttpFoundation\BinaryFileResponse; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\ResponseHeaderBag; +use Symfony\Component\HttpFoundation\StreamedResponse; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; +use Symfony\Component\Security\Csrf\CsrfToken; +use Symfony\Component\Form\Extension\Core\Type\FormType; +use Symfony\Component\Form\Form; +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Doctrine\Bundle\DoctrineBundle\Registry; + +/** + * Common features needed in controllers. + * + * @author Fabien Potencier + * + * @internal + */ +trait ControllerTrait +{ + /** + * Generates a URL from the given parameters. + * + * @param string $route The name of the route + * @param mixed $parameters An array of parameters + * @param int $referenceType The type of reference (one of the constants in UrlGeneratorInterface) + * + * @return string The generated URL + * + * @see UrlGeneratorInterface + */ + protected function generateUrl($route, $parameters = array(), $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH) + { + return $this->container->get('router')->generate($route, $parameters, $referenceType); + } + + /** + * Forwards the request to another controller. + * + * @param string $controller The controller name (a string like BlogBundle:Post:index) + * @param array $path An array of path parameters + * @param array $query An array of query parameters + * + * @return Response A Response instance + */ + protected function forward($controller, array $path = array(), array $query = array()) + { + $request = $this->container->get('request_stack')->getCurrentRequest(); + $path['_forwarded'] = $request->attributes; + $path['_controller'] = $controller; + $subRequest = $request->duplicate($query, null, $path); + + return $this->container->get('http_kernel')->handle($subRequest, HttpKernelInterface::SUB_REQUEST); + } + + /** + * Returns a RedirectResponse to the given URL. + * + * @param string $url The URL to redirect to + * @param int $status The status code to use for the Response + * + * @return RedirectResponse + */ + protected function redirect($url, $status = 302) + { + return new RedirectResponse($url, $status); + } + + /** + * Returns a RedirectResponse to the given route with the given parameters. + * + * @param string $route The name of the route + * @param array $parameters An array of parameters + * @param int $status The status code to use for the Response + * + * @return RedirectResponse + */ + protected function redirectToRoute($route, array $parameters = array(), $status = 302) + { + return $this->redirect($this->generateUrl($route, $parameters), $status); + } + + /** + * Returns a JsonResponse that uses the serializer component if enabled, or json_encode. + * + * @param mixed $data The response data + * @param int $status The status code to use for the Response + * @param array $headers Array of extra headers to add + * @param array $context Context to pass to serializer when using serializer component + * + * @return JsonResponse + */ + protected function json($data, $status = 200, $headers = array(), $context = array()) + { + if ($this->container->has('serializer')) { + $json = $this->container->get('serializer')->serialize($data, 'json', array_merge(array( + 'json_encode_options' => JsonResponse::DEFAULT_ENCODING_OPTIONS, + ), $context)); + + return new JsonResponse($json, $status, $headers, true); + } + + return new JsonResponse($data, $status, $headers); + } + + /** + * Returns a BinaryFileResponse object with original or customized file name and disposition header. + * + * @param \SplFileInfo|string $file File object or path to file to be sent as response + * @param string|null $fileName File name to be sent to response or null (will use original file name) + * @param string $disposition Disposition of response ("attachment" is default, other type is "inline") + * + * @return BinaryFileResponse + */ + protected function file($file, $fileName = null, $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT) + { + $response = new BinaryFileResponse($file); + $response->setContentDisposition($disposition, $fileName === null ? $response->getFile()->getFilename() : $fileName); + + return $response; + } + + /** + * Adds a flash message to the current session for type. + * + * @param string $type The type + * @param string $message The message + * + * @throws \LogicException + */ + protected function addFlash($type, $message) + { + if (!$this->container->has('session')) { + throw new \LogicException('You can not use the addFlash method if sessions are disabled.'); + } + + $this->container->get('session')->getFlashBag()->add($type, $message); + } + + /** + * Checks if the attributes are granted against the current authentication token and optionally supplied object. + * + * @param mixed $attributes The attributes + * @param mixed $object The object + * + * @return bool + * + * @throws \LogicException + */ + protected function isGranted($attributes, $object = null) + { + if (!$this->container->has('security.authorization_checker')) { + throw new \LogicException('The SecurityBundle is not registered in your application.'); + } + + return $this->container->get('security.authorization_checker')->isGranted($attributes, $object); + } + + /** + * Throws an exception unless the attributes are granted against the current authentication token and optionally + * supplied object. + * + * @param mixed $attributes The attributes + * @param mixed $object The object + * @param string $message The message passed to the exception + * + * @throws AccessDeniedException + */ + protected function denyAccessUnlessGranted($attributes, $object = null, $message = 'Access Denied.') + { + if (!$this->isGranted($attributes, $object)) { + $exception = $this->createAccessDeniedException($message); + $exception->setAttributes($attributes); + $exception->setSubject($object); + + throw $exception; + } + } + + /** + * Returns a rendered view. + * + * @param string $view The view name + * @param array $parameters An array of parameters to pass to the view + * + * @return string The rendered view + */ + protected function renderView($view, array $parameters = array()) + { + if ($this->container->has('templating')) { + return $this->container->get('templating')->render($view, $parameters); + } + + if (!$this->container->has('twig')) { + throw new \LogicException('You can not use the "renderView" method if the Templating Component or the Twig Bundle are not available.'); + } + + return $this->container->get('twig')->render($view, $parameters); + } + + /** + * Renders a view. + * + * @param string $view The view name + * @param array $parameters An array of parameters to pass to the view + * @param Response $response A response instance + * + * @return Response A Response instance + */ + protected function render($view, array $parameters = array(), Response $response = null) + { + if ($this->container->has('templating')) { + return $this->container->get('templating')->renderResponse($view, $parameters, $response); + } + + if (!$this->container->has('twig')) { + throw new \LogicException('You can not use the "render" method if the Templating Component or the Twig Bundle are not available.'); + } + + if (null === $response) { + $response = new Response(); + } + + $response->setContent($this->container->get('twig')->render($view, $parameters)); + + return $response; + } + + /** + * Streams a view. + * + * @param string $view The view name + * @param array $parameters An array of parameters to pass to the view + * @param StreamedResponse $response A response instance + * + * @return StreamedResponse A StreamedResponse instance + */ + protected function stream($view, array $parameters = array(), StreamedResponse $response = null) + { + if ($this->container->has('templating')) { + $templating = $this->container->get('templating'); + + $callback = function () use ($templating, $view, $parameters) { + $templating->stream($view, $parameters); + }; + } elseif ($this->container->has('twig')) { + $twig = $this->container->get('twig'); + + $callback = function () use ($twig, $view, $parameters) { + $twig->display($view, $parameters); + }; + } else { + throw new \LogicException('You can not use the "stream" method if the Templating Component or the Twig Bundle are not available.'); + } + + if (null === $response) { + return new StreamedResponse($callback); + } + + $response->setCallback($callback); + + return $response; + } + + /** + * Returns a NotFoundHttpException. + * + * This will result in a 404 response code. Usage example: + * + * throw $this->createNotFoundException('Page not found!'); + * + * @param string $message A message + * @param \Exception|null $previous The previous exception + * + * @return NotFoundHttpException + */ + protected function createNotFoundException($message = 'Not Found', \Exception $previous = null) + { + return new NotFoundHttpException($message, $previous); + } + + /** + * Returns an AccessDeniedException. + * + * This will result in a 403 response code. Usage example: + * + * throw $this->createAccessDeniedException('Unable to access this page!'); + * + * @param string $message A message + * @param \Exception|null $previous The previous exception + * + * @return AccessDeniedException + */ + protected function createAccessDeniedException($message = 'Access Denied.', \Exception $previous = null) + { + return new AccessDeniedException($message, $previous); + } + + /** + * Creates and returns a Form instance from the type of the form. + * + * @param string $type The fully qualified class name of the form type + * @param mixed $data The initial data for the form + * @param array $options Options for the form + * + * @return Form + */ + protected function createForm($type, $data = null, array $options = array()) + { + return $this->container->get('form.factory')->create($type, $data, $options); + } + + /** + * Creates and returns a form builder instance. + * + * @param mixed $data The initial data for the form + * @param array $options Options for the form + * + * @return FormBuilder + */ + protected function createFormBuilder($data = null, array $options = array()) + { + return $this->container->get('form.factory')->createBuilder(FormType::class, $data, $options); + } + + /** + * Shortcut to return the Doctrine Registry service. + * + * @return Registry + * + * @throws \LogicException If DoctrineBundle is not available + */ + protected function getDoctrine() + { + if (!$this->container->has('doctrine')) { + throw new \LogicException('The DoctrineBundle is not registered in your application.'); + } + + return $this->container->get('doctrine'); + } + + /** + * Get a user from the Security Token Storage. + * + * @return mixed + * + * @throws \LogicException If SecurityBundle is not available + * + * @see TokenInterface::getUser() + */ + protected function getUser() + { + if (!$this->container->has('security.token_storage')) { + throw new \LogicException('The SecurityBundle is not registered in your application.'); + } + + if (null === $token = $this->container->get('security.token_storage')->getToken()) { + return; + } + + if (!is_object($user = $token->getUser())) { + // e.g. anonymous authentication + return; + } + + return $user; + } + + /** + * Checks the validity of a CSRF token. + * + * @param string $id The id used when generating the token + * @param string $token The actual token sent with the request that should be validated + * + * @return bool + */ + protected function isCsrfTokenValid($id, $token) + { + if (!$this->container->has('security.csrf.token_manager')) { + throw new \LogicException('CSRF protection is not enabled in your application.'); + } + + return $this->container->get('security.csrf.token_manager')->isTokenValid(new CsrfToken($id, $token)); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/TemplateController.php b/src/Symfony/Bundle/FrameworkBundle/Controller/TemplateController.php index d222bc7ea37a2..1d4c44c6b4714 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/TemplateController.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/TemplateController.php @@ -36,8 +36,13 @@ class TemplateController implements ContainerAwareInterface */ public function templateAction($template, $maxAge = null, $sharedAge = null, $private = null) { - /** @var $response \Symfony\Component\HttpFoundation\Response */ - $response = $this->container->get('templating')->renderResponse($template); + if ($this->container->has('templating')) { + $response = $this->container->get('templating')->renderResponse($template); + } elseif ($this->container->has('twig')) { + $response = new Response($this->container->get('twig')->render($template)); + } else { + throw new \LogicException('You can not use the TemplateController if the Templating Component or the Twig Bundle are not available.'); + } if ($maxAge) { $response->setMaxAge($maxAge); diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.php new file mode 100644 index 0000000000000..508899379f691 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @internal + */ +class AddAnnotationsCachedReaderPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + // "annotations.cached_reader" is wired late so that any passes using + // "annotation_reader" at build time don't get any cache + if ($container->hasDefinition('annotations.cached_reader')) { + $reader = $container->getDefinition('annotations.cached_reader'); + $tags = $reader->getTags(); + + if (isset($tags['annotations.cached_reader'][0]['provider'])) { + if ($container->hasAlias($provider = $tags['annotations.cached_reader'][0]['provider'])) { + $provider = (string) $container->getAlias($provider); + } + $container->set('annotations.cached_reader', null); + $container->setDefinition('annotations.cached_reader', $reader->replaceArgument(1, new Reference($provider))); + } + } + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddCacheClearerPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddCacheClearerPass.php index 27b9b465c913c..88cfffd04f123 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddCacheClearerPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddCacheClearerPass.php @@ -32,7 +32,7 @@ public function process(ContainerBuilder $container) } $clearers = array(); - foreach ($container->findTaggedServiceIds('kernel.cache_clearer') as $id => $attributes) { + foreach ($container->findTaggedServiceIds('kernel.cache_clearer', true) as $id => $attributes) { $clearers[] = new Reference($id); } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConsoleCommandPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConsoleCommandPass.php index 37d7810ecac7a..d423648d05b1a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConsoleCommandPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConsoleCommandPass.php @@ -11,41 +11,17 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +@trigger_error(sprintf('%s is deprecated since version 3.3 and will be removed in 4.0. Use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass instead.', AddConsoleCommandPass::class), E_USER_DEPRECATED); + +use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass as BaseAddConsoleCommandPass; /** - * AddConsoleCommandPass. + * Registers console commands. * * @author Grégoire Pineau + * + * @deprecated since version 3.3, to be removed in 4.0. Use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass instead. */ -class AddConsoleCommandPass implements CompilerPassInterface +class AddConsoleCommandPass extends BaseAddConsoleCommandPass { - public function process(ContainerBuilder $container) - { - $commandServices = $container->findTaggedServiceIds('console.command'); - $serviceIds = array(); - - foreach ($commandServices as $id => $tags) { - $definition = $container->getDefinition($id); - - if ($definition->isAbstract()) { - throw new InvalidArgumentException(sprintf('The service "%s" tagged "console.command" must not be abstract.', $id)); - } - - $class = $container->getParameterBag()->resolveValue($definition->getClass()); - if (!is_subclass_of($class, 'Symfony\\Component\\Console\\Command\\Command')) { - if (!class_exists($class, false)) { - throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); - } - - throw new InvalidArgumentException(sprintf('The service "%s" tagged "console.command" must be a subclass of "Symfony\\Component\\Console\\Command\\Command".', $id)); - } - $container->setAlias($serviceId = 'console.command.'.strtolower(str_replace('\\', '_', $class)), $id); - $serviceIds[] = $definition->isPublic() ? $id : $serviceId; - } - - $container->setParameter('console.command.ids', $serviceIds); - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConstraintValidatorsPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConstraintValidatorsPass.php index 497ea08a4bf4e..3b89fa2651a80 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConstraintValidatorsPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConstraintValidatorsPass.php @@ -11,37 +11,13 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass as BaseAddConstraintValidatorsPass; -class AddConstraintValidatorsPass implements CompilerPassInterface -{ - public function process(ContainerBuilder $container) - { - if (!$container->hasDefinition('validator.validator_factory')) { - return; - } - - $validators = array(); - foreach ($container->findTaggedServiceIds('validator.constraint_validator') as $id => $attributes) { - if (isset($attributes[0]['alias'])) { - $validators[$attributes[0]['alias']] = $id; - } - - $definition = $container->getDefinition($id); - - if (!$definition->isPublic()) { - throw new InvalidArgumentException(sprintf('The service "%s" must be public as it can be lazy-loaded.', $id)); - } +@trigger_error(sprintf('The %s class is deprecated since version 3.3 and will be removed in 4.0. Use %s instead.', AddConstraintValidatorsPass::class, BaseAddConstraintValidatorsPass::class), E_USER_DEPRECATED); - if ($definition->isAbstract()) { - throw new InvalidArgumentException(sprintf('The service "%s" must not be abstract as it can be lazy-loaded.', $id)); - } - - $validators[$definition->getClass()] = $id; - } - - $container->getDefinition('validator.validator_factory')->replaceArgument(1, $validators); - } +/** + * @deprecated since version 3.3, to be removed in 4.0. Use {@link BaseAddConstraintValidatorsPass} instead + */ +class AddConstraintValidatorsPass extends BaseAddConstraintValidatorsPass +{ } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php index 6510d02c83063..d4103d8dff1a1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php @@ -30,7 +30,7 @@ public function process(ContainerBuilder $container) // routing if ($container->has('router')) { $definition = $container->findDefinition('router'); - foreach ($container->findTaggedServiceIds('routing.expression_language_provider') as $id => $attributes) { + foreach ($container->findTaggedServiceIds('routing.expression_language_provider', true) as $id => $attributes) { $definition->addMethodCall('addExpressionLanguageProvider', array(new Reference($id))); } } @@ -38,7 +38,7 @@ public function process(ContainerBuilder $container) // security if ($container->has('security.access.expression_voter')) { $definition = $container->findDefinition('security.access.expression_voter'); - foreach ($container->findTaggedServiceIds('security.expression_language_provider') as $id => $attributes) { + foreach ($container->findTaggedServiceIds('security.expression_language_provider', true) as $id => $attributes) { $definition->addMethodCall('addExpressionLanguageProvider', array(new Reference($id))); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddValidatorInitializersPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddValidatorInitializersPass.php index 6f58fc21bebf1..d71d87c1faad4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddValidatorInitializersPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddValidatorInitializersPass.php @@ -11,25 +11,13 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\Validator\DependencyInjection\AddValidatorInitializersPass as BaseAddValidatorsInitializerPass; -class AddValidatorInitializersPass implements CompilerPassInterface -{ - public function process(ContainerBuilder $container) - { - if (!$container->hasDefinition('validator.builder')) { - return; - } - - $validatorBuilder = $container->getDefinition('validator.builder'); - - $initializers = array(); - foreach ($container->findTaggedServiceIds('validator.initializer') as $id => $attributes) { - $initializers[] = new Reference($id); - } +@trigger_error(sprintf('The %s class is deprecated since version 3.3 and will be removed in 4.0. Use %s instead.', AddValidatorInitializersPass::class, BaseAddValidatorsInitializerPass::class), E_USER_DEPRECATED); - $validatorBuilder->addMethodCall('addObjectInitializers', array($initializers)); - } +/** + * @deprecated since version 3.3, to be removed in 4.0. Use {@link BaseAddValidatorInitializersPass} instead + */ +class AddValidatorInitializersPass extends BaseAddValidatorsInitializerPass +{ } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CacheCollectorPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CacheCollectorPass.php new file mode 100644 index 0000000000000..3d1908bf9bc92 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CacheCollectorPass.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\Cache\Adapter\TraceableAdapter; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Inject a data collector to all the cache services to be able to get detailed statistics. + * + * @author Tobias Nyholm + */ +class CacheCollectorPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('data_collector.cache')) { + return; + } + + $collectorDefinition = $container->getDefinition('data_collector.cache'); + foreach ($container->findTaggedServiceIds('cache.pool') as $id => $attributes) { + if ($container->getDefinition($id)->isAbstract()) { + continue; + } + + $container->register($id.'.recorder', TraceableAdapter::class) + ->setDecoratedService($id) + ->addArgument(new Reference($id.'.recorder.inner')) + ->setPublic(false); + + // Tell the collector to add the new instance + $collectorDefinition->addMethodCall('addInstance', array($id, new Reference($id))); + } + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolClearerPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolClearerPass.php index c859a6ba900a4..882f9b2dccf67 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolClearerPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolClearerPass.php @@ -27,31 +27,43 @@ final class CachePoolClearerPass implements CompilerPassInterface public function process(ContainerBuilder $container) { $container->getParameterBag()->remove('cache.prefix.seed'); + $poolsByClearer = array(); + $pools = array(); foreach ($container->findTaggedServiceIds('cache.pool') as $id => $attributes) { + $pools[$id] = new Reference($id); foreach (array_reverse($attributes) as $attr) { if (isset($attr['clearer'])) { - $clearer = $container->getDefinition($attr['clearer']); - $clearer->addMethodCall('addPool', array(new Reference($id))); + $poolsByClearer[$attr['clearer']][$id] = $pools[$id]; } - if (array_key_exists('clearer', $attr)) { + if (!empty($attr['unlazy'])) { + $container->getDefinition($id)->setLazy(false); + } + if (array_key_exists('clearer', $attr) || array_key_exists('unlazy', $attr)) { break; } } } + $container->getDefinition('cache.global_clearer')->addArgument($pools); + + foreach ($poolsByClearer as $clearer => $pools) { + $clearer = $container->getDefinition($clearer); + $clearer->addArgument($pools); + } + if (!$container->has('cache.annotations')) { return; } $factory = array(AbstractAdapter::class, 'createSystemCache'); - $annotationsPool = $container->getDefinition('cache.annotations'); + $annotationsPool = $container->findDefinition('cache.annotations'); if ($factory !== $annotationsPool->getFactory() || 4 !== count($annotationsPool->getArguments())) { return; } if ($container->has('monolog.logger.cache')) { $annotationsPool->addArgument(new Reference('monolog.logger.cache')); } elseif ($container->has('cache.system')) { - $systemPool = $container->getDefinition('cache.system'); + $systemPool = $container->findDefinition('cache.system'); if ($factory === $systemPool->getFactory() && 5 <= count($systemArgs = $systemPool->getArguments())) { $annotationsPool->addArgument($systemArgs[4]); } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php index 593b7782c66b0..e4a86dc6ef249 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php @@ -11,11 +11,11 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; -use Symfony\Component\Cache\Adapter\RedisAdapter; +use Symfony\Component\Cache\Adapter\AbstractAdapter; +use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; @@ -34,9 +34,8 @@ public function process(ContainerBuilder $container) } else { $seed = '_'.$container->getParameter('kernel.root_dir'); } - $seed .= '.'.$container->getParameter('kernel.name').'.'.$container->getParameter('kernel.environment').'.'.$container->getParameter('kernel.debug'); + $seed .= '.'.$container->getParameter('kernel.name').'.'.$container->getParameter('kernel.environment'); - $aliases = $container->getAliases(); $attributes = array( 'provider', 'namespace', @@ -47,8 +46,10 @@ public function process(ContainerBuilder $container) if ($pool->isAbstract()) { continue; } - while ($adapter instanceof DefinitionDecorator) { + $isLazy = $pool->isLazy(); + while ($adapter instanceof ChildDefinition) { $adapter = $container->findDefinition($adapter->getParent()); + $isLazy = $isLazy || $adapter->isLazy(); if ($t = $adapter->getTag('cache.pool')) { $tags[0] += $t[0]; } @@ -57,9 +58,9 @@ public function process(ContainerBuilder $container) $tags[0]['namespace'] = $this->getNamespace($seed, $id); } if (isset($tags[0]['clearer'])) { - $clearer = strtolower($tags[0]['clearer']); - while (isset($aliases[$clearer])) { - $clearer = (string) $aliases[$clearer]; + $clearer = $tags[0]['clearer']; + while ($container->hasAlias($clearer)) { + $clearer = (string) $container->getAlias($clearer); } } else { $clearer = null; @@ -80,8 +81,16 @@ public function process(ContainerBuilder $container) throw new InvalidArgumentException(sprintf('Invalid "cache.pool" tag for service "%s": accepted attributes are "clearer", "provider", "namespace" and "default_lifetime", found "%s".', $id, implode('", "', array_keys($tags[0])))); } + $attr = array(); if (null !== $clearer) { - $pool->addTag('cache.pool', array('clearer' => $clearer)); + $attr['clearer'] = $clearer; + } + if (!$isLazy) { + $pool->setLazy(true); + $attr['unlazy'] = true; + } + if ($attr) { + $pool->addTag('cache.pool', $attr); } } } @@ -96,13 +105,15 @@ private function getNamespace($seed, $id) */ public static function getServiceProvider(ContainerBuilder $container, $name) { - if (0 === strpos($name, 'redis://')) { + $container->resolveEnvPlaceholders($name, null, $usedEnvs); + + if ($usedEnvs || preg_match('#^[a-z]++://#', $name)) { $dsn = $name; if (!$container->hasDefinition($name = md5($dsn))) { - $definition = new Definition(\Redis::class); + $definition = new Definition(AbstractAdapter::class); $definition->setPublic(false); - $definition->setFactory(array(RedisAdapter::class, 'createConnection')); + $definition->setFactory(array(AbstractAdapter::class, 'createConnection')); $definition->setArguments(array($dsn)); $container->setDefinition($name, $definition); } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CompilerDebugDumpPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CompilerDebugDumpPass.php index f152ce8c509cb..714d01d01d5e5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CompilerDebugDumpPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CompilerDebugDumpPass.php @@ -11,12 +11,17 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; +@trigger_error(sprintf('The %s class is deprecated since version 3.3 and will be removed in 4.0.', CompilerDebugDumpPass::class), E_USER_DEPRECATED); + use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\Filesystem\Exception\IOException; use Symfony\Component\Filesystem\Filesystem; +/** + * @deprecated since version 3.3, to be removed in 4.0. + */ class CompilerDebugDumpPass implements CompilerPassInterface { public function process(ContainerBuilder $container) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ConfigCachePass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ConfigCachePass.php index d73e941f427ba..7fcea3b0e555d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ConfigCachePass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ConfigCachePass.php @@ -11,28 +11,18 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; -use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Config\DependencyInjection\ConfigCachePass as BaseConfigCachePass; + +@trigger_error(sprintf('The %s class is deprecated since version 3.3 and will be removed in 4.0. Use Symfony\Component\Config\DependencyInjection\ConfigCachePass instead.', ConfigCachePass::class), E_USER_DEPRECATED); /** * Adds services tagged config_cache.resource_checker to the config_cache_factory service, ordering them by priority. * + * @deprecated since version 3.3, to be removed in 4.0. Use {@link BaseConfigCachePass} instead. + * * @author Matthias Pigulla * @author Benjamin Klotz */ -class ConfigCachePass implements CompilerPassInterface +class ConfigCachePass extends BaseConfigCachePass { - use PriorityTaggedServiceTrait; - - public function process(ContainerBuilder $container) - { - $resourceCheckers = $this->findAndSortTaggedServices('config_cache.resource_checker', $container); - - if (empty($resourceCheckers)) { - return; - } - - $container->getDefinition('config_cache_factory')->replaceArgument(0, $resourceCheckers); - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ControllerArgumentValueResolverPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ControllerArgumentValueResolverPass.php index e087e691440f9..3f2baf6871c13 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ControllerArgumentValueResolverPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ControllerArgumentValueResolverPass.php @@ -11,27 +11,17 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; -use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\DependencyInjection\ControllerArgumentValueResolverPass as BaseControllerArgumentValueResolverPass; + +@trigger_error(sprintf('The %s class is deprecated since version 3.3 and will be removed in 4.0. Use %s instead.', ControllerArgumentValueResolverPass::class, BaseControllerArgumentValueResolverPass::class), E_USER_DEPRECATED); /** * Gathers and configures the argument value resolvers. * * @author Iltar van der Berg + * + * @deprecated since version 3.3, to be removed in 4.0. Use {@link BaseControllerArgumentValueResolverPass} */ -class ControllerArgumentValueResolverPass implements CompilerPassInterface +class ControllerArgumentValueResolverPass extends BaseControllerArgumentValueResolverPass { - use PriorityTaggedServiceTrait; - - public function process(ContainerBuilder $container) - { - if (!$container->hasDefinition('argument_resolver')) { - return; - } - - $definition = $container->getDefinition('argument_resolver'); - $argumentResolvers = $this->findAndSortTaggedServices('controller.argument_value_resolver', $container); - $definition->replaceArgument(1, $argumentResolvers); - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/DataCollectorTranslatorPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/DataCollectorTranslatorPass.php new file mode 100644 index 0000000000000..ee2bbb6521b17 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/DataCollectorTranslatorPass.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * @author Christian Flothmann + */ +class DataCollectorTranslatorPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->has('translator')) { + return; + } + + $translatorClass = $container->getParameterBag()->resolveValue($container->findDefinition('translator')->getClass()); + + if (!is_subclass_of($translatorClass, 'Symfony\Component\Translation\TranslatorBagInterface')) { + $container->removeDefinition('translator.data_collector'); + $container->removeDefinition('data_collector.translation'); + } + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/FormPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/FormPass.php index c248a079df2a9..fdbfd4297b225 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/FormPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/FormPass.php @@ -11,6 +11,8 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; +@trigger_error(sprintf('The %s class is deprecated since version 3.3 and will be removed in 4.0. Use Symfony\Component\Form\DependencyInjection\FormPass instead.', FormPass::class), E_USER_DEPRECATED); + use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; @@ -21,6 +23,8 @@ * arguments of the "form.extension" service. * * @author Bernhard Schussek + * + * @deprecated since version 3.3, to be removed in 4.0. Use FormPass in the Form component instead. */ class FormPass implements CompilerPassInterface { @@ -40,7 +44,7 @@ public function process(ContainerBuilder $container) foreach ($container->findTaggedServiceIds('form.type') as $serviceId => $tag) { $serviceDefinition = $container->getDefinition($serviceId); if (!$serviceDefinition->isPublic()) { - throw new InvalidArgumentException(sprintf('The service "%s" must be public as form types are lazy-loaded.', $serviceId)); + $serviceDefinition->setPublic(true); } // Support type access by FQCN @@ -55,7 +59,7 @@ public function process(ContainerBuilder $container) $serviceId = (string) $reference; $serviceDefinition = $container->getDefinition($serviceId); if (!$serviceDefinition->isPublic()) { - throw new InvalidArgumentException(sprintf('The service "%s" must be public as form type extensions are lazy-loaded.', $serviceId)); + $serviceDefinition->setPublic(true); } $tag = $serviceDefinition->getTag('form.type_extension'); @@ -75,7 +79,7 @@ public function process(ContainerBuilder $container) foreach ($guessers as $serviceId) { $serviceDefinition = $container->getDefinition($serviceId); if (!$serviceDefinition->isPublic()) { - throw new InvalidArgumentException(sprintf('The service "%s" must be public as form type guessers are lazy-loaded.', $serviceId)); + $serviceDefinition->setPublic(true); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/LoggingTranslatorPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/LoggingTranslatorPass.php index 74d586bf3b852..7d47dd2f9b132 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/LoggingTranslatorPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/LoggingTranslatorPass.php @@ -13,7 +13,10 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Component\Translation\TranslatorBagInterface; /** * @author Abdellatif Ait boudad @@ -31,7 +34,10 @@ public function process(ContainerBuilder $container) $definition = $container->getDefinition((string) $translatorAlias); $class = $container->getParameterBag()->resolveValue($definition->getClass()); - if (is_subclass_of($class, 'Symfony\Component\Translation\TranslatorInterface') && is_subclass_of($class, 'Symfony\Component\Translation\TranslatorBagInterface')) { + if (!$r = $container->getReflectionClass($class)) { + throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $translatorAlias)); + } + if ($r->isSubclassOf(TranslatorInterface::class) && $r->isSubclassOf(TranslatorBagInterface::class)) { $container->getDefinition('translator.logging')->setDecoratedService('translator'); $container->getDefinition('translation.warmer')->replaceArgument(0, new Reference('translator.logging.inner')); } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ProfilerPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ProfilerPass.php index 046407351ceeb..28cc83dfd3004 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ProfilerPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ProfilerPass.php @@ -33,7 +33,7 @@ public function process(ContainerBuilder $container) $collectors = new \SplPriorityQueue(); $order = PHP_INT_MAX; - foreach ($container->findTaggedServiceIds('data_collector') as $id => $attributes) { + foreach ($container->findTaggedServiceIds('data_collector', true) as $id => $attributes) { $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0; $template = null; diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/PropertyInfoPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/PropertyInfoPass.php index f05445f1a6276..3c73f9bf49507 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/PropertyInfoPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/PropertyInfoPass.php @@ -11,40 +11,17 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; -use Symfony\Component\DependencyInjection\ContainerBuilder; +@trigger_error(sprintf('The %s class is deprecated since version 3.3 and will be removed in 4.0. Use Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass instead.', PropertyInfoPass::class), E_USER_DEPRECATED); + +use Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass as BasePropertyInfoPass; /** * Adds extractors to the property_info service. * * @author Kévin Dunglas + * + * @deprecated since version 3.3, to be removed in 4.0. Use {@link BasePropertyInfoPass instead}. */ -class PropertyInfoPass implements CompilerPassInterface +class PropertyInfoPass extends BasePropertyInfoPass { - use PriorityTaggedServiceTrait; - - /** - * {@inheritdoc} - */ - public function process(ContainerBuilder $container) - { - if (!$container->hasDefinition('property_info')) { - return; - } - - $definition = $container->getDefinition('property_info'); - - $listExtractors = $this->findAndSortTaggedServices('property_info.list_extractor', $container); - $definition->replaceArgument(0, $listExtractors); - - $typeExtractors = $this->findAndSortTaggedServices('property_info.type_extractor', $container); - $definition->replaceArgument(1, $typeExtractors); - - $descriptionExtractors = $this->findAndSortTaggedServices('property_info.description_extractor', $container); - $definition->replaceArgument(2, $descriptionExtractors); - - $accessExtractors = $this->findAndSortTaggedServices('property_info.access_extractor', $container); - $definition->replaceArgument(3, $accessExtractors); - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/RoutingResolverPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/RoutingResolverPass.php index 6ff7c124cdac0..bac782115b557 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/RoutingResolverPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/RoutingResolverPass.php @@ -11,27 +11,17 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\Routing\DependencyInjection\RoutingResolverPass as BaseRoutingResolverPass; + +@trigger_error(sprintf('The %s class is deprecated since version 3.3 and will be removed in 4.0. Use %s instead.', RoutingResolverPass::class, BaseRoutingResolverPass::class), E_USER_DEPRECATED); /** * Adds tagged routing.loader services to routing.resolver service. * * @author Fabien Potencier + * + * @deprecated since version 3.3, to be removed in 4.0. Use {@link BaseRoutingResolverPass} */ -class RoutingResolverPass implements CompilerPassInterface +class RoutingResolverPass extends BaseRoutingResolverPass { - public function process(ContainerBuilder $container) - { - if (false === $container->hasDefinition('routing.resolver')) { - return; - } - - $definition = $container->getDefinition('routing.resolver'); - - foreach ($container->findTaggedServiceIds('routing.loader') as $id => $attributes) { - $definition->addMethodCall('addLoader', array(new Reference($id))); - } - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/SerializerPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/SerializerPass.php index d3d3019998c84..d30e8eba43a17 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/SerializerPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/SerializerPass.php @@ -11,40 +11,18 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; -use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\Exception\RuntimeException; +@trigger_error(sprintf('The %s class is deprecated since version 3.3 and will be removed in 4.0. Use Symfony\Component\Serializer\DependencyInjection\SerializerPass instead.', SerializerPass::class), E_USER_DEPRECATED); + +use Symfony\Component\Serializer\DependencyInjection\SerializerPass as BaseSerializerPass; /** * Adds all services with the tags "serializer.encoder" and "serializer.normalizer" as * encoders and normalizers to the Serializer service. * + * @deprecated since version 3.3, to be removed in 4.0. Use {@link BaseSerializerPass} instead. + * * @author Javier Lopez */ -class SerializerPass implements CompilerPassInterface +class SerializerPass extends BaseSerializerPass { - use PriorityTaggedServiceTrait; - - public function process(ContainerBuilder $container) - { - if (!$container->hasDefinition('serializer')) { - return; - } - - // Looks for all the services tagged "serializer.normalizer" and adds them to the Serializer service - $normalizers = $this->findAndSortTaggedServices('serializer.normalizer', $container); - - if (empty($normalizers)) { - throw new RuntimeException('You must tag at least one service as "serializer.normalizer" to use the Serializer service'); - } - $container->getDefinition('serializer')->replaceArgument(0, $normalizers); - - // Looks for all the services tagged "serializer.encoders" and adds them to the Serializer service - $encoders = $this->findAndSortTaggedServices('serializer.encoder', $container); - if (empty($encoders)) { - throw new RuntimeException('You must tag at least one service as "serializer.encoder" to use the Serializer service'); - } - $container->getDefinition('serializer')->replaceArgument(1, $encoders); - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TemplatingPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TemplatingPass.php index 4ef67a42b6fe6..8e2e4d0dbfb34 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TemplatingPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TemplatingPass.php @@ -12,6 +12,7 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface as FrameworkBundleEngineInterface; +use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\Templating\EngineInterface as ComponentEngineInterface; @@ -25,13 +26,13 @@ public function process(ContainerBuilder $container) } if ($container->hasAlias('templating')) { - $definition = $container->findDefinition('templating'); - $definition->setAutowiringTypes(array(ComponentEngineInterface::class, FrameworkBundleEngineInterface::class)); + $container->setAlias(ComponentEngineInterface::class, new Alias('templating', false)); + $container->setAlias(FrameworkBundleEngineInterface::class, new Alias('templating', false)); } if ($container->hasDefinition('templating.engine.php')) { $helpers = array(); - foreach ($container->findTaggedServiceIds('templating.helper') as $id => $attributes) { + foreach ($container->findTaggedServiceIds('templating.helper', true) as $id => $attributes) { if (isset($attributes[0]['alias'])) { $helpers[$attributes[0]['alias']] = $id; } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslationDumperPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslationDumperPass.php index f7c09748b4f7e..541b06b31e254 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslationDumperPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslationDumperPass.php @@ -28,7 +28,7 @@ public function process(ContainerBuilder $container) $definition = $container->getDefinition('translation.writer'); - foreach ($container->findTaggedServiceIds('translation.dumper') as $id => $attributes) { + foreach ($container->findTaggedServiceIds('translation.dumper', true) as $id => $attributes) { $definition->addMethodCall('addDumper', array($attributes[0]['alias'], new Reference($id))); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslationExtractorPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslationExtractorPass.php index fc8a66d24fa06..4dc8d3985ce68 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslationExtractorPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslationExtractorPass.php @@ -29,7 +29,7 @@ public function process(ContainerBuilder $container) $definition = $container->getDefinition('translation.extractor'); - foreach ($container->findTaggedServiceIds('translation.extractor') as $id => $attributes) { + foreach ($container->findTaggedServiceIds('translation.extractor', true) as $id => $attributes) { if (!isset($attributes[0]['alias'])) { throw new RuntimeException(sprintf('The alias for the tag "translation.extractor" of service "%s" must be set.', $id)); } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslatorPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslatorPass.php index 4e450166afa44..29e251f274339 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslatorPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslatorPass.php @@ -14,6 +14,7 @@ use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; class TranslatorPass implements CompilerPassInterface { @@ -24,7 +25,9 @@ public function process(ContainerBuilder $container) } $loaders = array(); - foreach ($container->findTaggedServiceIds('translation.loader') as $id => $attributes) { + $loaderRefs = array(); + foreach ($container->findTaggedServiceIds('translation.loader', true) as $id => $attributes) { + $loaderRefs[$id] = new Reference($id); $loaders[$id][] = $attributes[0]['alias']; if (isset($attributes[0]['legacy-alias'])) { $loaders[$id][] = $attributes[0]['legacy-alias']; @@ -35,11 +38,15 @@ public function process(ContainerBuilder $container) $definition = $container->getDefinition('translation.loader'); foreach ($loaders as $id => $formats) { foreach ($formats as $format) { - $definition->addMethodCall('addLoader', array($format, new Reference($id))); + $definition->addMethodCall('addLoader', array($format, $loaderRefs[$id])); } } } - $container->findDefinition('translator.default')->replaceArgument(2, $loaders); + $container + ->findDefinition('translator.default') + ->replaceArgument(0, ServiceLocatorTagPass::register($container, $loaderRefs)) + ->replaceArgument(3, $loaders) + ; } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php index ed852fd041942..ffc19c49dfeff 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php @@ -23,6 +23,9 @@ class UnusedTagsPass implements CompilerPassInterface { private $whitelist = array( 'console.command', + 'container.service_locator', + 'container.service_subscriber', + 'controller.service_arguments', 'config_cache.resource_checker', 'data_collector', 'form.type', @@ -53,8 +56,6 @@ class UnusedTagsPass implements CompilerPassInterface public function process(ContainerBuilder $container) { - $compiler = $container->getCompiler(); - $formatter = $compiler->getLoggingFormatter(); $tags = array_unique(array_merge($container->findTags(), $this->whitelist)); foreach ($container->findUnusedTags() as $tag) { @@ -81,7 +82,7 @@ public function process(ContainerBuilder $container) $message .= sprintf(' Did you mean "%s"?', implode('", "', $candidates)); } - $compiler->addLogMessage($formatter->format($this, $message)); + $container->log($this, $message); } } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ValidateWorkflowsPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ValidateWorkflowsPass.php index a2f695992926d..6599f1f1a0300 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ValidateWorkflowsPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ValidateWorkflowsPass.php @@ -11,54 +11,15 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Exception\RuntimeException; -use Symfony\Component\Workflow\Validator\DefinitionValidatorInterface; -use Symfony\Component\Workflow\Validator\StateMachineValidator; -use Symfony\Component\Workflow\Validator\WorkflowValidator; +use Symfony\Component\Workflow\DependencyInjection\ValidateWorkflowsPass as BaseValidateWorkflowsPass; + +@trigger_error(sprintf('The %s class is deprecated since version 3.3 and will be removed in 4.0. Use %s instead.', ValidateWorkflowsPass::class, BaseValidateWorkflowsPass::class), E_USER_DEPRECATED); /** * @author Tobias Nyholm + * + * @deprecated since version 3.3, to be removed in 4.0. Use {@link BaseValidateWorkflowsPass} instead */ -class ValidateWorkflowsPass implements CompilerPassInterface +class ValidateWorkflowsPass extends BaseValidateWorkflowsPass { - public function process(ContainerBuilder $container) - { - $taggedServices = $container->findTaggedServiceIds('workflow.definition'); - foreach ($taggedServices as $id => $tags) { - $definition = $container->get($id); - foreach ($tags as $tag) { - if (!array_key_exists('name', $tag)) { - throw new RuntimeException(sprintf('The "name" for the tag "workflow.definition" of service "%s" must be set.', $id)); - } - if (!array_key_exists('type', $tag)) { - throw new RuntimeException(sprintf('The "type" for the tag "workflow.definition" of service "%s" must be set.', $id)); - } - if (!array_key_exists('marking_store', $tag)) { - throw new RuntimeException(sprintf('The "marking_store" for the tag "workflow.definition" of service "%s" must be set.', $id)); - } - - $this->createValidator($tag)->validate($definition, $tag['name']); - } - } - } - - /** - * @param array $tag - * - * @return DefinitionValidatorInterface - */ - private function createValidator($tag) - { - if ('state_machine' === $tag['type']) { - return new StateMachineValidator(); - } - - if ('single_state' === $tag['marking_store']) { - return new WorkflowValidator(true); - } - - return new WorkflowValidator(); - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 5c6208e6e46f2..87d722b91e4d6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -11,9 +11,17 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection; +use Doctrine\Common\Annotations\Annotation; +use Symfony\Bundle\FullStack; +use Symfony\Component\Asset\Package; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Form\Form; +use Symfony\Component\Serializer\Serializer; +use Symfony\Component\Translation\Translator; +use Symfony\Component\Validator\Validation; +use Symfony\Component\WebLink\HttpHeaderSerializer; /** * FrameworkExtension configuration structure. @@ -57,34 +65,10 @@ public function getConfigTreeBuilder() ->info("Set true to enable support for the '_method' request parameter to determine the intended HTTP method on POST requests. Note: When using the HttpCache, you need to call the method in your front controller instead") ->defaultTrue() ->end() - ->arrayNode('trusted_proxies') + ->arrayNode('trusted_proxies') // @deprecated in version 3.3, to be removed in 4.0 ->beforeNormalization() - ->ifTrue(function ($v) { return !is_array($v) && null !== $v; }) - ->then(function ($v) { return is_bool($v) ? array() : preg_split('/\s*,\s*/', $v); }) - ->end() - ->prototype('scalar') - ->validate() - ->ifTrue(function ($v) { - if (empty($v)) { - return false; - } - - if (false !== strpos($v, '/')) { - if ('0.0.0.0/0' === $v) { - return false; - } - - list($v, $mask) = explode('/', $v, 2); - - if (strcmp($mask, (int) $mask) || $mask < 1 || $mask > (false !== strpos($v, ':') ? 128 : 32)) { - return true; - } - } - - return !filter_var($v, FILTER_VALIDATE_IP); - }) - ->thenInvalid('Invalid proxy IP "%s"') - ->end() + ->always() + ->thenInvalid('The "framework.trusted_proxies" configuration key has been removed in Symfony 3.3. Use the Request::setTrustedProxies() method in your front controller instead.') ->end() ->end() ->scalarNode('ide')->defaultNull()->end() @@ -117,6 +101,7 @@ public function getConfigTreeBuilder() $this->addPropertyInfoSection($rootNode); $this->addCacheSection($rootNode); $this->addPhpErrorsSection($rootNode); + $this->addWebLinkSection($rootNode); return $treeBuilder; } @@ -138,7 +123,7 @@ private function addFormSection(ArrayNodeDefinition $rootNode) ->children() ->arrayNode('form') ->info('form configuration') - ->canBeEnabled() + ->{!class_exists(FullStack::class) && class_exists(Form::class) ? 'canBeDisabled' : 'canBeEnabled'}() ->children() ->arrayNode('csrf_protection') ->treatFalseLike(array('enabled' => false)) @@ -240,9 +225,11 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode) ->fixXmlConfig('place') ->fixXmlConfig('transition') ->children() + ->arrayNode('audit_trail') + ->canBeEnabled() + ->end() ->enumNode('type') ->values(array('workflow', 'state_machine')) - ->defaultValue('workflow') ->end() ->arrayNode('marking_store') ->fixXmlConfig('argument') @@ -268,12 +255,11 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode) ->thenInvalid('"type" and "service" cannot be used together.') ->end() ->validate() - ->ifTrue(function ($v) { return isset($v['arguments']) && isset($v['service']); }) + ->ifTrue(function ($v) { return !empty($v['arguments']) && isset($v['service']); }) ->thenInvalid('"arguments" and "service" cannot be used together.') ->end() ->end() ->arrayNode('supports') - ->isRequired() ->beforeNormalization() ->ifString() ->then(function ($v) { return array($v); }) @@ -286,7 +272,12 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode) ->end() ->end() ->end() - ->scalarNode('initial_place')->defaultNull()->end() + ->scalarNode('support_strategy') + ->cannotBeEmpty() + ->end() + ->scalarNode('initial_place') + ->defaultNull() + ->end() ->arrayNode('places') ->isRequired() ->requiresAtLeastOneElement() @@ -295,11 +286,38 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode) ->end() ->end() ->arrayNode('transitions') - ->useAttributeAsKey('name') + ->beforeNormalization() + ->always() + ->then(function ($transitions) { + // It's an indexed array, we let the validation occurs + if (isset($transitions[0])) { + return $transitions; + } + + foreach ($transitions as $name => $transition) { + if (array_key_exists('name', $transition)) { + continue; + } + $transition['name'] = $name; + $transitions[$name] = $transition; + } + + return $transitions; + }) + ->end() ->isRequired() ->requiresAtLeastOneElement() ->prototype('array') ->children() + ->scalarNode('name') + ->isRequired() + ->cannotBeEmpty() + ->end() + ->scalarNode('guard') + ->cannotBeEmpty() + ->info('An expression to block the transition') + ->example('is_fully_authenticated() and has_role(\'ROLE_JOURNALIST\') and subject.getTitle() == \'My first article\'') + ->end() ->arrayNode('from') ->beforeNormalization() ->ifString() @@ -324,6 +342,18 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode) ->end() ->end() ->end() + ->validate() + ->ifTrue(function ($v) { + return $v['supports'] && isset($v['support_strategy']); + }) + ->thenInvalid('"supports" and "support_strategy" cannot be used together.') + ->end() + ->validate() + ->ifTrue(function ($v) { + return !$v['supports'] && !isset($v['support_strategy']); + }) + ->thenInvalid('"supports" or "support_strategy" should be configured.') + ->end() ->end() ->end() ->end() @@ -483,12 +513,13 @@ private function addAssetsSection(ArrayNodeDefinition $rootNode) ->children() ->arrayNode('assets') ->info('assets configuration') - ->canBeEnabled() + ->{!class_exists(FullStack::class) && class_exists(Package::class) ? 'canBeDisabled' : 'canBeEnabled'}() ->fixXmlConfig('base_url') ->children() ->scalarNode('version_strategy')->defaultNull()->end() ->scalarNode('version')->defaultNull()->end() ->scalarNode('version_format')->defaultValue('%%s?%%s')->end() + ->scalarNode('json_manifest_path')->defaultNull()->end() ->scalarNode('base_path')->defaultValue('')->end() ->arrayNode('base_urls') ->requiresAtLeastOneElement() @@ -505,6 +536,18 @@ private function addAssetsSection(ArrayNodeDefinition $rootNode) }) ->thenInvalid('You cannot use both "version_strategy" and "version" at the same time under "assets".') ->end() + ->validate() + ->ifTrue(function ($v) { + return isset($v['version_strategy']) && isset($v['json_manifest_path']); + }) + ->thenInvalid('You cannot use both "version_strategy" and "json_manifest_path" at the same time under "assets".') + ->end() + ->validate() + ->ifTrue(function ($v) { + return isset($v['version']) && isset($v['json_manifest_path']); + }) + ->thenInvalid('You cannot use both "version" and "json_manifest_path" at the same time under "assets".') + ->end() ->fixXmlConfig('package') ->children() ->arrayNode('packages') @@ -520,6 +563,7 @@ private function addAssetsSection(ArrayNodeDefinition $rootNode) ->end() ->end() ->scalarNode('version_format')->defaultNull()->end() + ->scalarNode('json_manifest_path')->defaultNull()->end() ->scalarNode('base_path')->defaultValue('')->end() ->arrayNode('base_urls') ->requiresAtLeastOneElement() @@ -536,6 +580,18 @@ private function addAssetsSection(ArrayNodeDefinition $rootNode) }) ->thenInvalid('You cannot use both "version_strategy" and "version" at the same time under "assets" packages.') ->end() + ->validate() + ->ifTrue(function ($v) { + return isset($v['version_strategy']) && isset($v['json_manifest_path']); + }) + ->thenInvalid('You cannot use both "version_strategy" and "json_manifest_path" at the same time under "assets" packages.') + ->end() + ->validate() + ->ifTrue(function ($v) { + return isset($v['version']) && isset($v['json_manifest_path']); + }) + ->thenInvalid('You cannot use both "version" and "json_manifest_path" at the same time under "assets" packages.') + ->end() ->end() ->end() ->end() @@ -550,7 +606,7 @@ private function addTranslatorSection(ArrayNodeDefinition $rootNode) ->children() ->arrayNode('translator') ->info('translator configuration') - ->canBeEnabled() + ->{!class_exists(FullStack::class) && class_exists(Translator::class) ? 'canBeDisabled' : 'canBeEnabled'}() ->fixXmlConfig('fallback') ->fixXmlConfig('path') ->children() @@ -575,10 +631,10 @@ private function addValidationSection(ArrayNodeDefinition $rootNode) ->children() ->arrayNode('validation') ->info('validation configuration') - ->canBeEnabled() + ->{!class_exists(FullStack::class) && class_exists(Validation::class) ? 'canBeDisabled' : 'canBeEnabled'}() ->children() ->scalarNode('cache')->end() - ->booleanNode('enable_annotations')->defaultFalse()->end() + ->booleanNode('enable_annotations')->{!class_exists(FullStack::class) && class_exists(Annotation::class) ? 'defaultTrue' : 'defaultFalse'}()->end() ->arrayNode('static_method') ->defaultValue(array('loadValidatorMetadata')) ->prototype('scalar')->end() @@ -590,6 +646,15 @@ private function addValidationSection(ArrayNodeDefinition $rootNode) ->end() ->scalarNode('translation_domain')->defaultValue('validators')->end() ->booleanNode('strict_email')->defaultFalse()->end() + ->arrayNode('mapping') + ->addDefaultsIfNotSet() + ->fixXmlConfig('path') + ->children() + ->arrayNode('paths') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() ->end() ->end() ->end() @@ -602,7 +667,7 @@ private function addAnnotationsSection(ArrayNodeDefinition $rootNode) ->children() ->arrayNode('annotations') ->info('annotation configuration') - ->canBeDisabled() + ->{class_exists(Annotation::class) ? 'canBeDisabled' : 'canBeEnabled'}() ->children() ->scalarNode('cache')->defaultValue('php_array')->end() ->scalarNode('file_cache_dir')->defaultValue('%kernel.cache_dir%/annotations')->end() @@ -619,11 +684,21 @@ private function addSerializerSection(ArrayNodeDefinition $rootNode) ->children() ->arrayNode('serializer') ->info('serializer configuration') - ->canBeEnabled() + ->{!class_exists(FullStack::class) && class_exists(Serializer::class) ? 'canBeDisabled' : 'canBeEnabled'}() ->children() - ->booleanNode('enable_annotations')->defaultFalse()->end() + ->booleanNode('enable_annotations')->{!class_exists(FullStack::class) && class_exists(Annotation::class) ? 'defaultTrue' : 'defaultFalse'}()->end() ->scalarNode('cache')->end() ->scalarNode('name_converter')->end() + ->scalarNode('circular_reference_handler')->end() + ->arrayNode('mapping') + ->addDefaultsIfNotSet() + ->fixXmlConfig('path') + ->children() + ->arrayNode('paths') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() ->end() ->end() ->end() @@ -683,6 +758,7 @@ private function addCacheSection(ArrayNodeDefinition $rootNode) ->scalarNode('default_doctrine_provider')->end() ->scalarNode('default_psr6_provider')->end() ->scalarNode('default_redis_provider')->defaultValue('redis://localhost')->end() + ->scalarNode('default_memcached_provider')->defaultValue('memcached://localhost')->end() ->arrayNode('pools') ->useAttributeAsKey('name') ->prototype('array') @@ -730,4 +806,16 @@ private function addPhpErrorsSection(ArrayNodeDefinition $rootNode) ->end() ; } + + private function addWebLinkSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('web_link') + ->info('web links configuration') + ->{!class_exists(FullStack::class) && class_exists(HttpHeaderSerializer::class) ? 'canBeDisabled' : 'canBeEnabled'}() + ->end() + ->end() + ; + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 61a7d7a9170d3..c65c1f8e740be 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -13,29 +13,51 @@ use Doctrine\Common\Annotations\Reader; use Symfony\Bridge\Monolog\Processor\DebugProcessor; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\Cache\Adapter\AdapterInterface; +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\Config\Resource\DirectoryResource; +use Symfony\Component\Config\ResourceCheckerInterface; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\Exception\LogicException; -use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; -use Symfony\Component\Config\Resource\FileResource; -use Symfony\Component\Config\Resource\DirectoryResource; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Symfony\Component\Finder\Finder; +use Symfony\Component\Form\FormTypeGuesserInterface; +use Symfony\Component\Form\FormTypeInterface; +use Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface; +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; +use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface; use Symfony\Component\HttpKernel\DependencyInjection\Extension; -use Symfony\Component\Config\FileLocator; use Symfony\Component\PropertyAccess\PropertyAccessor; -use Symfony\Component\Serializer\Encoder\YamlEncoder; +use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface; +use Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface; +use Symfony\Component\PropertyInfo\PropertyListExtractorInterface; +use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; use Symfony\Component\Serializer\Encoder\CsvEncoder; +use Symfony\Component\Serializer\Encoder\EncoderInterface; +use Symfony\Component\Serializer\Encoder\YamlEncoder; use Symfony\Component\Serializer\Mapping\Factory\CacheClassMetadataFactory; use Symfony\Component\Serializer\Normalizer\DataUriNormalizer; use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer; use Symfony\Component\Serializer\Normalizer\JsonSerializableNormalizer; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +use Symfony\Component\Validator\ConstraintValidatorInterface; +use Symfony\Component\Validator\ObjectInitializerInterface; +use Symfony\Component\WebLink\HttpHeaderSerializer; use Symfony\Component\Workflow; -use Symfony\Component\Yaml\Yaml; /** * FrameworkExtension. @@ -71,8 +93,20 @@ public function load(array $configs, ContainerBuilder $container) $loader->load('web.xml'); $loader->load('services.xml'); + + if (PHP_VERSION_ID < 70000) { + $definition = $container->getDefinition('kernel.class_cache.cache_warmer'); + $definition->addTag('kernel.cache_warmer'); + // Ignore deprecation for PHP versions below 7.0 + $definition->setDeprecated(false); + } + $loader->load('fragment_renderer.xml'); + if (class_exists(Application::class)) { + $loader->load('console.xml'); + } + // Property access is used by both the Form and the Validator component $loader->load('property_access.xml'); @@ -83,16 +117,25 @@ public function load(array $configs, ContainerBuilder $container) $config = $this->processConfiguration($configuration, $configs); $this->annotationsConfigEnabled = $this->isConfigEnabled($container, $config['annotations']); + $this->translationConfigEnabled = $this->isConfigEnabled($container, $config['translator']); // A translator must always be registered (as support is included by - // default in the Form component). If disabled, an identity translator - // will be used and everything will still work as expected. - if (class_exists('Symfony\Component\Translation\Translator') || $this->isConfigEnabled($container, $config['form'])) { - if (!class_exists('Symfony\Component\Translation\Translator')) { + // default in the Form and Validator component). If disabled, an identity + // translator will be used and everything will still work as expected. + if ($this->isConfigEnabled($container, $config['translator']) || $this->isConfigEnabled($container, $config['form']) || $this->isConfigEnabled($container, $config['validation'])) { + if (!class_exists('Symfony\Component\Translation\Translator') && $this->isConfigEnabled($container, $config['translator'])) { + throw new LogicException('Translation support cannot be enabled as the Translation component is not installed.'); + } + + if (!class_exists('Symfony\Component\Translation\Translator') && $this->isConfigEnabled($container, $config['form'])) { throw new LogicException('Form support cannot be enabled as the Translation component is not installed.'); } - $loader->load('translation.xml'); + if (!class_exists('Symfony\Component\Translation\Translator') && $this->isConfigEnabled($container, $config['validation'])) { + throw new LogicException('Validation support cannot be enabled as the Translation component is not installed.'); + } + + $loader->load('identity_translator.xml'); } if (isset($config['secret'])) { @@ -101,7 +144,6 @@ public function load(array $configs, ContainerBuilder $container) $container->setParameter('kernel.http_method_override', $config['http_method_override']); $container->setParameter('kernel.trusted_hosts', $config['trusted_hosts']); - $container->setParameter('kernel.trusted_proxies', $config['trusted_proxies']); $container->setParameter('kernel.default_locale', $config['default_locale']); if (!$container->hasParameter('debug.file_link_format')) { @@ -111,7 +153,7 @@ public function load(array $configs, ContainerBuilder $container) 'macvim' => 'mvim://open?url=file://%%f&line=%%l', 'emacs' => 'emacs://open?url=file://%%f&line=%%l', 'sublime' => 'subl://open?url=file://%%f&line=%%l', - 'phpstorm' => 'phpstorm://open?url=file://%%f&line=%%l', + 'phpstorm' => 'phpstorm://open?file=%%f&line=%%l', ); $ide = $config['ide']; @@ -165,7 +207,7 @@ public function load(array $configs, ContainerBuilder $container) $this->registerEsiConfiguration($config['esi'], $container, $loader); $this->registerSsiConfiguration($config['ssi'], $container, $loader); $this->registerFragmentsConfiguration($config['fragments'], $container, $loader); - $this->registerTranslatorConfiguration($config['translator'], $container); + $this->registerTranslatorConfiguration($config['translator'], $container, $loader); $this->registerProfilerConfiguration($config['profiler'], $container, $loader); $this->registerCacheConfiguration($config['cache'], $container); $this->registerWorkflowConfiguration($config['workflows'], $container, $loader); @@ -186,6 +228,14 @@ public function load(array $configs, ContainerBuilder $container) $this->registerPropertyInfoConfiguration($config['property_info'], $container, $loader); } + if ($this->isConfigEnabled($container, $config['web_link'])) { + if (!class_exists(HttpHeaderSerializer::class)) { + throw new LogicException('WebLink support cannot be enabled as the WebLink component is not installed.'); + } + + $loader->load('web_link.xml'); + } + $this->addAnnotatedClassesToCompile(array( '**Bundle\\Controller\\', '**Bundle\\Entity\\', @@ -194,47 +244,85 @@ public function load(array $configs, ContainerBuilder $container) 'Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller', )); - $this->addClassesToCompile(array( - 'Symfony\\Component\\Config\\ConfigCache', - 'Symfony\\Component\\Config\\FileLocator', - - 'Symfony\\Component\\Debug\\ErrorHandler', - - 'Symfony\\Component\\DependencyInjection\\ContainerAwareInterface', - 'Symfony\\Component\\DependencyInjection\\Container', - - 'Symfony\\Component\\EventDispatcher\\Event', - 'Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher', - - 'Symfony\\Component\\HttpFoundation\\Response', - 'Symfony\\Component\\HttpFoundation\\ResponseHeaderBag', - - 'Symfony\\Component\\HttpKernel\\EventListener\\ResponseListener', - 'Symfony\\Component\\HttpKernel\\EventListener\\RouterListener', - 'Symfony\\Component\\HttpKernel\\Bundle\\Bundle', - 'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver', - 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver', - 'Symfony\\Component\\HttpKernel\\ControllerMetadata\\ArgumentMetadata', - 'Symfony\\Component\\HttpKernel\\ControllerMetadata\\ArgumentMetadataFactory', - 'Symfony\\Component\\HttpKernel\\Event\\KernelEvent', - 'Symfony\\Component\\HttpKernel\\Event\\FilterControllerEvent', - 'Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent', - 'Symfony\\Component\\HttpKernel\\Event\\GetResponseEvent', - 'Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent', - 'Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent', - 'Symfony\\Component\\HttpKernel\\HttpKernel', - 'Symfony\\Component\\HttpKernel\\KernelEvents', - 'Symfony\\Component\\HttpKernel\\Config\\FileLocator', - - 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerNameParser', - 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerResolver', - - // Cannot be included because annotations will parse the big compiled class file - // 'Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller', - - // cannot be included as commands are discovered based on the path to this class via Reflection - // 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle', - )); + $container->registerForAutoconfiguration(Command::class) + ->addTag('console.command'); + $container->registerForAutoconfiguration(ResourceCheckerInterface::class) + ->addTag('config_cache.resource_checker'); + $container->registerForAutoconfiguration(ServiceSubscriberInterface::class) + ->addTag('container.service_subscriber'); + $container->registerForAutoconfiguration(AbstractController::class) + ->addTag('controller.service_arguments'); + $container->registerForAutoconfiguration(Controller::class) + ->addTag('controller.service_arguments'); + $container->registerForAutoconfiguration(DataCollectorInterface::class) + ->addTag('data_collector'); + $container->registerForAutoconfiguration(FormTypeInterface::class) + ->addTag('form.type'); + $container->registerForAutoconfiguration(FormTypeGuesserInterface::class) + ->addTag('form.type_guesser'); + $container->registerForAutoconfiguration(CacheClearerInterface::class) + ->addTag('kernel.cache_clearer'); + $container->registerForAutoconfiguration(CacheWarmerInterface::class) + ->addTag('kernel.cache_warmer'); + $container->registerForAutoconfiguration(EventSubscriberInterface::class) + ->addTag('kernel.event_subscriber'); + $container->registerForAutoconfiguration(PropertyListExtractorInterface::class) + ->addTag('property_info.list_extractor'); + $container->registerForAutoconfiguration(PropertyTypeExtractorInterface::class) + ->addTag('property_info.type_extractor'); + $container->registerForAutoconfiguration(PropertyDescriptionExtractorInterface::class) + ->addTag('property_info.description_extractor'); + $container->registerForAutoconfiguration(PropertyAccessExtractorInterface::class) + ->addTag('property_info.access_extractor'); + $container->registerForAutoconfiguration(EncoderInterface::class) + ->addTag('serializer.encoder'); + $container->registerForAutoconfiguration(NormalizerInterface::class) + ->addTag('serializer.normalizer'); + $container->registerForAutoconfiguration(ConstraintValidatorInterface::class) + ->addTag('validator.constraint_validator'); + $container->registerForAutoconfiguration(ObjectInitializerInterface::class) + ->addTag('validator.initializer'); + + if (PHP_VERSION_ID < 70000) { + $this->addClassesToCompile(array( + 'Symfony\\Component\\Config\\ConfigCache', + 'Symfony\\Component\\Config\\FileLocator', + + 'Symfony\\Component\\Debug\\ErrorHandler', + + 'Symfony\\Component\\DependencyInjection\\ContainerAwareInterface', + 'Symfony\\Component\\DependencyInjection\\Container', + + 'Symfony\\Component\\EventDispatcher\\Event', + 'Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher', + + 'Symfony\\Component\\HttpKernel\\EventListener\\ResponseListener', + 'Symfony\\Component\\HttpKernel\\EventListener\\RouterListener', + 'Symfony\\Component\\HttpKernel\\Bundle\\Bundle', + 'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver', + 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver', + 'Symfony\\Component\\HttpKernel\\ControllerMetadata\\ArgumentMetadata', + 'Symfony\\Component\\HttpKernel\\ControllerMetadata\\ArgumentMetadataFactory', + 'Symfony\\Component\\HttpKernel\\Event\\KernelEvent', + 'Symfony\\Component\\HttpKernel\\Event\\FilterControllerEvent', + 'Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent', + 'Symfony\\Component\\HttpKernel\\Event\\GetResponseEvent', + 'Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent', + 'Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent', + 'Symfony\\Component\\HttpKernel\\HttpKernel', + 'Symfony\\Component\\HttpKernel\\KernelEvents', + 'Symfony\\Component\\HttpKernel\\Config\\FileLocator', + + 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerNameParser', + 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerResolver', + + // Cannot be included because annotations will parse the big compiled class file + // 'Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller', + + // cannot be included as commands are discovered based on the path to this class via Reflection + // 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle', + )); + } } /** @@ -340,6 +428,7 @@ private function registerProfilerConfiguration(array $config, ContainerBuilder $ $loader->load('profiling.xml'); $loader->load('collectors.xml'); + $loader->load('cache_debug.xml'); if ($this->formConfigEnabled) { $loader->load('form_debug.xml'); @@ -400,21 +489,29 @@ private function registerWorkflowConfiguration(array $workflows, ContainerBuilde return; } + if (!class_exists(Workflow\Workflow::class)) { + throw new LogicException('Workflow support cannot be enabled as the Workflow component is not installed.'); + } + $loader->load('workflow.xml'); $registryDefinition = $container->getDefinition('workflow.registry'); foreach ($workflows as $name => $workflow) { + if (!array_key_exists('type', $workflow)) { + $workflow['type'] = 'workflow'; + @trigger_error(sprintf('The "type" option of the "framework.workflows.%s" configuration entry must be defined since Symfony 3.3. The default value will be "state_machine" in Symfony 4.0.', $name), E_USER_DEPRECATED); + } $type = $workflow['type']; $transitions = array(); - foreach ($workflow['transitions'] as $transitionName => $transition) { + foreach ($workflow['transitions'] as $transition) { if ($type === 'workflow') { - $transitions[] = new Definition(Workflow\Transition::class, array($transitionName, $transition['from'], $transition['to'])); + $transitions[] = new Definition(Workflow\Transition::class, array($transition['name'], $transition['from'], $transition['to'])); } elseif ($type === 'state_machine') { foreach ($transition['from'] as $from) { foreach ($transition['to'] as $to) { - $transitions[] = new Definition(Workflow\Transition::class, array($transitionName, $from, $to)); + $transitions[] = new Definition(Workflow\Transition::class, array($transition['name'], $from, $to)); } } } @@ -436,7 +533,7 @@ private function registerWorkflowConfiguration(array $workflows, ContainerBuilde // Create MarkingStore if (isset($workflow['marking_store']['type'])) { - $markingStoreDefinition = new DefinitionDecorator('workflow.marking_store.'.$workflow['marking_store']['type']); + $markingStoreDefinition = new ChildDefinition('workflow.marking_store.'.$workflow['marking_store']['type']); foreach ($workflow['marking_store']['arguments'] as $argument) { $markingStoreDefinition->addArgument($argument); } @@ -445,7 +542,7 @@ private function registerWorkflowConfiguration(array $workflows, ContainerBuilde } // Create Workflow - $workflowDefinition = new DefinitionDecorator(sprintf('%s.abstract', $type)); + $workflowDefinition = new ChildDefinition(sprintf('%s.abstract', $type)); $workflowDefinition->replaceArgument(0, $definitionDefinition); if (isset($markingStoreDefinition)) { $workflowDefinition->replaceArgument(1, $markingStoreDefinition); @@ -458,8 +555,54 @@ private function registerWorkflowConfiguration(array $workflows, ContainerBuilde $container->setDefinition(sprintf('%s.definition', $workflowId), $definitionDefinition); // Add workflow to Registry - foreach ($workflow['supports'] as $supportedClass) { - $registryDefinition->addMethodCall('add', array(new Reference($workflowId), $supportedClass)); + if ($workflow['supports']) { + foreach ($workflow['supports'] as $supportedClassName) { + $strategyDefinition = new Definition(Workflow\SupportStrategy\ClassInstanceSupportStrategy::class, array($supportedClassName)); + $strategyDefinition->setPublic(false); + $registryDefinition->addMethodCall('add', array(new Reference($workflowId), $strategyDefinition)); + } + } elseif (isset($workflow['support_strategy'])) { + $registryDefinition->addMethodCall('add', array(new Reference($workflowId), new Reference($workflow['support_strategy']))); + } + + // Enable the AuditTrail + if ($workflow['audit_trail']['enabled']) { + $listener = new Definition(Workflow\EventListener\AuditTrailListener::class); + $listener->addTag('monolog.logger', array('channel' => 'workflow')); + $listener->addTag('kernel.event_listener', array('event' => sprintf('workflow.%s.leave', $name), 'method' => 'onLeave')); + $listener->addTag('kernel.event_listener', array('event' => sprintf('workflow.%s.transition', $name), 'method' => 'onTransition')); + $listener->addTag('kernel.event_listener', array('event' => sprintf('workflow.%s.enter', $name), 'method' => 'onEnter')); + $listener->addArgument(new Reference('logger')); + $container->setDefinition(sprintf('%s.listener.audit_trail', $workflowId), $listener); + } + + // Add Guard Listener + $guard = new Definition(Workflow\EventListener\GuardListener::class); + $configuration = array(); + foreach ($workflow['transitions'] as $transitionName => $config) { + if (!isset($config['guard'])) { + continue; + } + + if (!class_exists(ExpressionLanguage::class)) { + throw new LogicException('Cannot guard workflows as the ExpressionLanguage component is not installed.'); + } + + $eventName = sprintf('workflow.%s.guard.%s', $name, $transitionName); + $guard->addTag('kernel.event_listener', array('event' => $eventName, 'method' => 'onTransition')); + $configuration[$eventName] = $config['guard']; + } + if ($configuration) { + $guard->setArguments(array( + $configuration, + new Reference('workflow.security.expression_language'), + new Reference('security.token_storage'), + new Reference('security.authorization_checker'), + new Reference('security.authentication.trust_resolver'), + new Reference('security.role_hierarchy'), + )); + + $container->setDefinition(sprintf('%s.listener.guard', $workflowId), $guard); } } } @@ -479,12 +622,6 @@ private function registerDebugConfiguration(array $config, ContainerBuilder $con if ($debug) { $loader->load('debug.xml'); - - // replace the regular event_dispatcher service with the debug one - $definition = $container->findDefinition('event_dispatcher'); - $definition->setPublic(false); - $container->setDefinition('debug.event_dispatcher.parent', $definition); - $container->setAlias('event_dispatcher', 'debug.event_dispatcher'); } $definition = $container->findDefinition('debug.debug_handlers_listener'); @@ -531,13 +668,15 @@ private function registerRouterConfiguration(array $config, ContainerBuilder $co $container->setParameter('request_listener.http_port', $config['http_port']); $container->setParameter('request_listener.https_port', $config['https_port']); - $this->addClassesToCompile(array( - 'Symfony\\Component\\Routing\\Generator\\UrlGenerator', - 'Symfony\\Component\\Routing\\RequestContext', - 'Symfony\\Component\\Routing\\Router', - 'Symfony\\Bundle\\FrameworkBundle\\Routing\\RedirectableUrlMatcher', - $container->findDefinition('router.default')->getClass(), - )); + if (PHP_VERSION_ID < 70000) { + $this->addClassesToCompile(array( + 'Symfony\\Component\\Routing\\Generator\\UrlGenerator', + 'Symfony\\Component\\Routing\\RequestContext', + 'Symfony\\Component\\Routing\\Router', + 'Symfony\\Bundle\\FrameworkBundle\\Routing\\RedirectableUrlMatcher', + $container->findDefinition('router.default')->getClass(), + )); + } } /** @@ -580,20 +719,22 @@ private function registerSessionConfiguration(array $config, ContainerBuilder $c $container->setParameter('session.save_path', $config['save_path']); - $this->addClassesToCompile(array( - 'Symfony\\Bundle\\FrameworkBundle\\EventListener\\SessionListener', - 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage', - 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\PhpBridgeSessionStorage', - 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NativeFileSessionHandler', - 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\AbstractProxy', - 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\SessionHandlerProxy', - $container->getDefinition('session')->getClass(), - )); - - if ($container->hasDefinition($config['storage_id'])) { + if (PHP_VERSION_ID < 70000) { $this->addClassesToCompile(array( - $container->findDefinition('session.storage')->getClass(), + 'Symfony\\Component\\HttpKernel\\EventListener\\SessionListener', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\PhpBridgeSessionStorage', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NativeFileSessionHandler', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\AbstractProxy', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\SessionHandlerProxy', + $container->getDefinition('session')->getClass(), )); + + if ($container->hasDefinition($config['storage_id'])) { + $this->addClassesToCompile(array( + $container->findDefinition('session.storage')->getClass(), + )); + } } $container->setParameter('session.metadata.update_threshold', $config['metadata_update_threshold']); @@ -663,12 +804,14 @@ private function registerTemplatingConfiguration(array $config, ContainerBuilder $container->setDefinition('templating.loader', $loaderCache); } - $this->addClassesToCompile(array( - 'Symfony\\Bundle\\FrameworkBundle\\Templating\\GlobalVariables', - 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateReference', - 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateNameParser', - $container->findDefinition('templating.locator')->getClass(), - )); + if (PHP_VERSION_ID < 70000) { + $this->addClassesToCompile(array( + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\GlobalVariables', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateReference', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateNameParser', + $container->findDefinition('templating.locator')->getClass(), + )); + } $container->setParameter('templating.engines', $config['engines']); $engines = array_map(function ($engine) { return new Reference('templating.engine.'.$engine); }, $config['engines']); @@ -701,17 +844,23 @@ private function registerTemplatingConfiguration(array $config, ContainerBuilder $container->setAlias('debug.templating.engine.php', 'templating.engine.php'); } - $this->addClassesToCompile(array( - 'Symfony\\Component\\Templating\\Storage\\FileStorage', - 'Symfony\\Bundle\\FrameworkBundle\\Templating\\PhpEngine', - 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Loader\\FilesystemLoader', - )); + if (PHP_VERSION_ID < 70000) { + $this->addClassesToCompile(array( + 'Symfony\\Component\\Templating\\Storage\\FileStorage', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\PhpEngine', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Loader\\FilesystemLoader', + )); + } if ($container->has('assets.packages')) { $container->getDefinition('templating.helper.assets')->replaceArgument(0, new Reference('assets.packages')); } else { $container->removeDefinition('templating.helper.assets'); } + + if (!$this->translationConfigEnabled) { + $container->removeDefinition('templating.helper.translator'); + } } } @@ -731,7 +880,7 @@ private function registerAssetsConfiguration(array $config, ContainerBuilder $co if ($config['version_strategy']) { $defaultVersion = new Reference($config['version_strategy']); } else { - $defaultVersion = $this->createVersion($container, $config['version'], $config['version_format'], '_default'); + $defaultVersion = $this->createVersion($container, $config['version'], $config['version_format'], $config['json_manifest_path'], '_default'); } $defaultPackage = $this->createPackageDefinition($config['base_path'], $config['base_urls'], $defaultVersion); @@ -741,11 +890,14 @@ private function registerAssetsConfiguration(array $config, ContainerBuilder $co foreach ($config['packages'] as $name => $package) { if (null !== $package['version_strategy']) { $version = new Reference($package['version_strategy']); - } elseif (!array_key_exists('version', $package)) { + } elseif (!array_key_exists('version', $package) && null === $package['json_manifest_path']) { + // if neither version nor json_manifest_path are specified, use the default $version = $defaultVersion; } else { + // let format fallback to main version_format $format = $package['version_format'] ?: $config['version_format']; - $version = $this->createVersion($container, $package['version'], $format, $name); + $version = isset($package['version']) ? $package['version'] : null; + $version = $this->createVersion($container, $version, $format, $package['json_manifest_path'], $name); } $container->setDefinition('assets._package_'.$name, $this->createPackageDefinition($package['base_path'], $package['base_urls'], $version)); @@ -767,39 +919,39 @@ private function createPackageDefinition($basePath, array $baseUrls, Reference $ throw new \LogicException('An asset package cannot have base URLs and base paths.'); } - if (!$baseUrls) { - $package = new DefinitionDecorator('assets.path_package'); - - return $package - ->setPublic(false) - ->replaceArgument(0, $basePath) - ->replaceArgument(1, $version) - ; - } - - $package = new DefinitionDecorator('assets.url_package'); - - return $package + $package = new ChildDefinition($baseUrls ? 'assets.url_package' : 'assets.path_package'); + $package ->setPublic(false) - ->replaceArgument(0, $baseUrls) + ->replaceArgument(0, $baseUrls ?: $basePath) ->replaceArgument(1, $version) ; + + return $package; } - private function createVersion(ContainerBuilder $container, $version, $format, $name) + private function createVersion(ContainerBuilder $container, $version, $format, $jsonManifestPath, $name) { - if (null === $version) { - return new Reference('assets.empty_version_strategy'); + // Configuration prevents $version and $jsonManifestPath from being set + if (null !== $version) { + $def = new ChildDefinition('assets.static_version_strategy'); + $def + ->replaceArgument(0, $version) + ->replaceArgument(1, $format) + ; + $container->setDefinition('assets._version_'.$name, $def); + + return new Reference('assets._version_'.$name); } - $def = new DefinitionDecorator('assets.static_version_strategy'); - $def - ->replaceArgument(0, $version) - ->replaceArgument(1, $format) - ; - $container->setDefinition('assets._version_'.$name, $def); + if (null !== $jsonManifestPath) { + $def = new ChildDefinition('assets.json_manifest_version_strategy'); + $def->replaceArgument(0, $jsonManifestPath); + $container->setDefinition('assets._version_'.$name, $def); - return new Reference('assets._version_'.$name); + return new Reference('assets._version_'.$name); + } + + return new Reference('assets.empty_version_strategy'); } /** @@ -808,17 +960,13 @@ private function createVersion(ContainerBuilder $container, $version, $format, $ * @param array $config A translator configuration array * @param ContainerBuilder $container A ContainerBuilder instance */ - private function registerTranslatorConfiguration(array $config, ContainerBuilder $container) + private function registerTranslatorConfiguration(array $config, ContainerBuilder $container, LoaderInterface $loader) { if (!$this->isConfigEnabled($container, $config)) { return; } - if (!class_exists('Symfony\Component\Translation\Translator')) { - throw new LogicException('Translation support cannot be enabled as the Translator component is not installed.'); - } - - $this->translationConfigEnabled = true; + $loader->load('translation.xml'); // Use the "real" translator instead of the identity default $container->setAlias('translator', 'translator.default'); @@ -845,36 +993,32 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder $dirs[] = dirname(dirname($r->getFileName())).'/Resources/translations'; } $rootDir = $container->getParameter('kernel.root_dir'); - foreach ($container->getParameter('kernel.bundles') as $bundle => $class) { - $reflection = new \ReflectionClass($class); - if (is_dir($dir = dirname($reflection->getFileName()).'/Resources/translations')) { + foreach ($container->getParameter('kernel.bundles_metadata') as $name => $bundle) { + if ($container->fileExists($dir = $bundle['path'].'/Resources/translations')) { $dirs[] = $dir; } - if (is_dir($dir = $rootDir.sprintf('/Resources/%s/translations', $bundle))) { + if ($container->fileExists($dir = $rootDir.sprintf('/Resources/%s/translations', $name))) { $dirs[] = $dir; } } foreach ($config['paths'] as $dir) { - if (is_dir($dir)) { + if ($container->fileExists($dir)) { $dirs[] = $dir; } else { throw new \UnexpectedValueException(sprintf('%s defined in translator.paths does not exist or is not a directory', $dir)); } } - if (is_dir($dir = $rootDir.'/Resources/translations')) { + if ($container->fileExists($dir = $rootDir.'/Resources/translations')) { $dirs[] = $dir; } // Register translation resources if ($dirs) { - foreach ($dirs as $dir) { - $container->addResource(new DirectoryResource($dir)); - } - $files = array(); $finder = Finder::create() + ->followLinks() ->files() ->filter(function (\SplFileInfo $file) { return 2 === substr_count($file->getBasename(), '.') && preg_match('/\.\w+$/', $file->getBasename()); @@ -892,11 +1036,11 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder } $options = array_merge( - $translator->getArgument(3), + $translator->getArgument(4), array('resource_files' => $files) ); - $translator->replaceArgument(3, $options); + $translator->replaceArgument(4, $options); } } @@ -913,19 +1057,25 @@ private function registerValidationConfiguration(array $config, ContainerBuilder return; } + if (!class_exists('Symfony\Component\Validator\Validation')) { + throw new LogicException('Validation support cannot be enabled as the Validator component is not installed.'); + } + $loader->load('validator.xml'); $validatorBuilder = $container->getDefinition('validator.builder'); $container->setParameter('validator.translation_domain', $config['translation_domain']); - list($xmlMappings, $yamlMappings) = $this->getValidatorMappingFiles($container); - if (count($xmlMappings) > 0) { - $validatorBuilder->addMethodCall('addXmlMappings', array($xmlMappings)); + $files = array('xml' => array(), 'yml' => array()); + $this->registerValidatorMapping($container, $config, $files); + + if (!empty($files['xml'])) { + $validatorBuilder->addMethodCall('addXmlMappings', array($files['xml'])); } - if (count($yamlMappings) > 0) { - $validatorBuilder->addMethodCall('addYamlMappings', array($yamlMappings)); + if (!empty($files['yml'])) { + $validatorBuilder->addMethodCall('addYamlMappings', array($files['yml'])); } $definition = $container->findDefinition('validator.email'); @@ -959,44 +1109,58 @@ private function registerValidationConfiguration(array $config, ContainerBuilder } } - private function getValidatorMappingFiles(ContainerBuilder $container) + private function registerValidatorMapping(ContainerBuilder $container, array $config, array &$files) { - $files = array(array(), array()); + $fileRecorder = function ($extension, $path) use (&$files) { + $files['yaml' === $extension ? 'yml' : $extension][] = $path; + }; if (interface_exists('Symfony\Component\Form\FormInterface')) { $reflClass = new \ReflectionClass('Symfony\Component\Form\FormInterface'); - $files[0][] = dirname($reflClass->getFileName()).'/Resources/config/validation.xml'; - $container->addResource(new FileResource($files[0][0])); + $fileRecorder('xml', dirname($reflClass->getFileName()).'/Resources/config/validation.xml'); } - $bundles = $container->getParameter('kernel.bundles'); - foreach ($bundles as $bundle) { - $reflection = new \ReflectionClass($bundle); - $dirname = dirname($reflection->getFileName()); + foreach ($container->getParameter('kernel.bundles_metadata') as $bundle) { + $dirname = $bundle['path']; - if (is_file($file = $dirname.'/Resources/config/validation.xml')) { - $files[0][] = $file; - $container->addResource(new FileResource($file)); + if ($container->fileExists($file = $dirname.'/Resources/config/validation.yml', false)) { + $fileRecorder('yml', $file); } - if (is_file($file = $dirname.'/Resources/config/validation.yml')) { - $files[1][] = $file; - $container->addResource(new FileResource($file)); + if ($container->fileExists($file = $dirname.'/Resources/config/validation.xml', false)) { + $fileRecorder('xml', $file); } - if (is_dir($dir = $dirname.'/Resources/config/validation')) { - foreach (Finder::create()->files()->in($dir)->name('*.xml') as $file) { - $files[0][] = $file->getPathname(); - } - foreach (Finder::create()->files()->in($dir)->name('*.yml') as $file) { - $files[1][] = $file->getPathname(); - } - - $container->addResource(new DirectoryResource($dir)); + if ($container->fileExists($dir = $dirname.'/Resources/config/validation', '/^$/')) { + $this->registerMappingFilesFromDir($dir, $fileRecorder); } } - return $files; + $this->registerMappingFilesFromConfig($container, $config, $fileRecorder); + } + + private function registerMappingFilesFromDir($dir, callable $fileRecorder) + { + foreach (Finder::create()->followLinks()->files()->in($dir)->name('/\.(xml|ya?ml)$/') as $file) { + $fileRecorder($file->getExtension(), $file->getRealPath()); + } + } + + private function registerMappingFilesFromConfig(ContainerBuilder $container, array $config, callable $fileRecorder) + { + foreach ($config['mapping']['paths'] as $path) { + if (is_dir($path)) { + $this->registerMappingFilesFromDir($path, $fileRecorder); + $container->addResource(new DirectoryResource($path, '/^$/')); + } elseif ($container->fileExists($path, false)) { + if (!preg_match('/\.(xml|ya?ml)$/', $path, $matches)) { + throw new \RuntimeException(sprintf('Unsupported mapping type in "%s", supported types are XML & Yaml.', $path)); + } + $fileRecorder($matches[1], $path); + } else { + throw new \RuntimeException(sprintf('Could not open file or directory "%s".', $path)); + } + } } private function registerAnnotationsConfiguration(array $config, ContainerBuilder $container, $loader) @@ -1021,10 +1185,12 @@ private function registerAnnotationsConfiguration(array $config, ContainerBuilde $definition = $container->findDefinition('annotations.cache_warmer'); $definition->addTag('kernel.cache_warmer'); - $this->addClassesToCompile(array( - 'Symfony\Component\Cache\Adapter\PhpArrayAdapter', - 'Symfony\Component\Cache\DoctrineProvider', - )); + if (PHP_VERSION_ID < 70000) { + $this->addClassesToCompile(array( + 'Symfony\Component\Cache\Adapter\PhpArrayAdapter', + 'Symfony\Component\Cache\DoctrineProvider', + )); + } } elseif ('file' === $config['cache']) { $cacheDir = $container->getParameterBag()->resolveValue($config['file_cache_dir']); @@ -1042,11 +1208,13 @@ private function registerAnnotationsConfiguration(array $config, ContainerBuilde $container ->getDefinition('annotations.cached_reader') - ->replaceArgument(1, new Reference($cacheService)) ->replaceArgument(2, $config['debug']) - ->addAutowiringType(Reader::class) + ->addTag('annotations.cached_reader', array('provider' => $cacheService)) ; $container->setAlias('annotation_reader', 'annotations.cached_reader'); + $container->setAlias(Reader::class, new Alias('annotations.cached_reader', false)); + } else { + $container->removeDefinition('annotations.cached_reader'); } } @@ -1146,45 +1314,30 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder $serializerLoaders[] = $annotationLoader; } - $bundles = $container->getParameter('kernel.bundles'); - foreach ($bundles as $bundle) { - $reflection = new \ReflectionClass($bundle); - $dirname = dirname($reflection->getFileName()); + $fileRecorder = function ($extension, $path) use (&$serializerLoaders) { + $definition = new Definition(in_array($extension, array('yaml', 'yml')) ? 'Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader' : 'Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader', array($path)); + $definition->setPublic(false); + $serializerLoaders[] = $definition; + }; - if (is_file($file = $dirname.'/Resources/config/serialization.xml')) { - $definition = new Definition('Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader', array($file)); - $definition->setPublic(false); + foreach ($container->getParameter('kernel.bundles_metadata') as $bundle) { + $dirname = $bundle['path']; - $serializerLoaders[] = $definition; - $container->addResource(new FileResource($file)); + if ($container->fileExists($file = $dirname.'/Resources/config/serialization.xml', false)) { + $fileRecorder('xml', $file); } - if (is_file($file = $dirname.'/Resources/config/serialization.yml')) { - $definition = new Definition('Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader', array($file)); - $definition->setPublic(false); - - $serializerLoaders[] = $definition; - $container->addResource(new FileResource($file)); + if ($container->fileExists($file = $dirname.'/Resources/config/serialization.yml', false)) { + $fileRecorder('yml', $file); } - if (is_dir($dir = $dirname.'/Resources/config/serialization')) { - foreach (Finder::create()->files()->in($dir)->name('*.xml') as $file) { - $definition = new Definition('Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader', array($file->getPathname())); - $definition->setPublic(false); - - $serializerLoaders[] = $definition; - } - foreach (Finder::create()->files()->in($dir)->name('*.yml') as $file) { - $definition = new Definition('Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader', array($file->getPathname())); - $definition->setPublic(false); - - $serializerLoaders[] = $definition; - } - - $container->addResource(new DirectoryResource($dir)); + if ($container->fileExists($dir = $dirname.'/Resources/config/serialization')) { + $this->registerMappingFilesFromDir($dir, $fileRecorder); } } + $this->registerMappingFilesFromConfig($container, $config, $fileRecorder); + $chainLoader->replaceArgument(0, $serializerLoaders); $container->getDefinition('serializer.mapping.cache_warmer')->replaceArgument(0, $serializerLoaders); @@ -1216,6 +1369,10 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder if (isset($config['name_converter']) && $config['name_converter']) { $container->getDefinition('serializer.normalizer.object')->replaceArgument(1, new Reference($config['name_converter'])); } + + if (isset($config['circular_reference_handler']) && $config['circular_reference_handler']) { + $container->getDefinition('serializer.normalizer.object')->addMethodCall('setCircularReferenceHandler', array(new Reference($config['circular_reference_handler']))); + } } /** @@ -1239,7 +1396,6 @@ private function registerPropertyInfoConfiguration(array $config, ContainerBuild private function registerCacheConfiguration(array $config, ContainerBuilder $container) { $version = substr(str_replace('/', '-', base64_encode(hash('sha256', uniqid(mt_rand(), true), true))), 0, 22); - $container->getDefinition('cache.annotations')->replaceArgument(2, $version); $container->getDefinition('cache.adapter.apcu')->replaceArgument(2, $version); $container->getDefinition('cache.adapter.system')->replaceArgument(2, $version); $container->getDefinition('cache.adapter.filesystem')->replaceArgument(2, $config['directory']); @@ -1247,7 +1403,11 @@ private function registerCacheConfiguration(array $config, ContainerBuilder $con if (isset($config['prefix_seed'])) { $container->setParameter('cache.prefix.seed', $config['prefix_seed']); } - foreach (array('doctrine', 'psr6', 'redis') as $name) { + if ($container->hasParameter('cache.prefix.seed')) { + // Inline any env vars referenced in the parameter + $container->setParameter('cache.prefix.seed', $container->resolveEnvPlaceholders($container->getParameter('cache.prefix.seed'), true)); + } + foreach (array('doctrine', 'psr6', 'redis', 'memcached') as $name) { if (isset($config[$name = 'default_'.$name.'_provider'])) { $container->setAlias('cache.'.$name, new Alias(Compiler\CachePoolPass::getServiceProvider($container, $config[$name]), false)); } @@ -1259,7 +1419,7 @@ private function registerCacheConfiguration(array $config, ContainerBuilder $con ); } foreach ($config['pools'] as $name => $pool) { - $definition = new DefinitionDecorator($pool['adapter']); + $definition = new ChildDefinition($pool['adapter']); $definition->setPublic($pool['public']); unset($pool['adapter'], $pool['public']); @@ -1270,17 +1430,25 @@ private function registerCacheConfiguration(array $config, ContainerBuilder $con if (method_exists(PropertyAccessor::class, 'createCache')) { $propertyAccessDefinition = $container->register('cache.property_access', AdapterInterface::class); $propertyAccessDefinition->setPublic(false); - $propertyAccessDefinition->setFactory(array(PropertyAccessor::class, 'createCache')); - $propertyAccessDefinition->setArguments(array(null, null, $version, new Reference('logger', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))); - $propertyAccessDefinition->addTag('cache.pool', array('clearer' => 'cache.default_clearer')); - $propertyAccessDefinition->addTag('monolog.logger', array('channel' => 'cache')); + + if (!$container->getParameter('kernel.debug')) { + $propertyAccessDefinition->setFactory(array(PropertyAccessor::class, 'createCache')); + $propertyAccessDefinition->setArguments(array(null, null, $version, new Reference('logger', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))); + $propertyAccessDefinition->addTag('cache.pool', array('clearer' => 'cache.default_clearer')); + $propertyAccessDefinition->addTag('monolog.logger', array('channel' => 'cache')); + } else { + $propertyAccessDefinition->setClass(ArrayAdapter::class); + $propertyAccessDefinition->setArguments(array(0, false)); + } } - $this->addClassesToCompile(array( - 'Symfony\Component\Cache\Adapter\ApcuAdapter', - 'Symfony\Component\Cache\Adapter\FilesystemAdapter', - 'Symfony\Component\Cache\CacheItem', - )); + if (PHP_VERSION_ID < 70000) { + $this->addClassesToCompile(array( + 'Symfony\Component\Cache\Adapter\ApcuAdapter', + 'Symfony\Component\Cache\Adapter\FilesystemAdapter', + 'Symfony\Component\Cache\CacheItem', + )); + } } /** diff --git a/src/Symfony/Bundle/FrameworkBundle/EventListener/SessionListener.php b/src/Symfony/Bundle/FrameworkBundle/EventListener/SessionListener.php index 6c248d8a42880..92db8ece73ee1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/EventListener/SessionListener.php +++ b/src/Symfony/Bundle/FrameworkBundle/EventListener/SessionListener.php @@ -11,19 +11,20 @@ namespace Symfony\Bundle\FrameworkBundle\EventListener; -use Symfony\Component\HttpKernel\EventListener\SessionListener as BaseSessionListener; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpKernel\EventListener\AbstractSessionListener; + +@trigger_error(sprintf('The %s class is deprecated since version 3.3 and will be removed in 4.0. Use Symfony\Component\HttpKernel\EventListener\SessionListener instead.', SessionListener::class), E_USER_DEPRECATED); /** * Sets the session in the request. * * @author Fabien Potencier + * + * @deprecated since version 3.3, to be removed in 4.0. Use Symfony\Component\HttpKernel\EventListener\SessionListener instead */ -class SessionListener extends BaseSessionListener +class SessionListener extends AbstractSessionListener { - /** - * @var ContainerInterface - */ private $container; public function __construct(ContainerInterface $container) diff --git a/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php b/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php index b32faa2f05668..703be8ff3beda 100644 --- a/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php +++ b/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php @@ -11,15 +11,19 @@ namespace Symfony\Bundle\FrameworkBundle\EventListener; -use Symfony\Component\HttpKernel\EventListener\TestSessionListener as BaseTestSessionListener; +@trigger_error(sprintf('The %s class is deprecated since version 3.3 and will be removed in 4.0. Use Symfony\Component\HttpKernel\EventListener\TestSessionListener instead.', TestSessionListener::class), E_USER_DEPRECATED); + +use Symfony\Component\HttpKernel\EventListener\AbstractTestSessionListener; use Symfony\Component\DependencyInjection\ContainerInterface; /** * TestSessionListener. * * @author Fabien Potencier + * + * @deprecated since version 3.3, to be removed in 4.0. */ -class TestSessionListener extends BaseTestSessionListener +class TestSessionListener extends AbstractTestSessionListener { protected $container; diff --git a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php index 4c20d146cb4bf..1794a86f92488 100644 --- a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php +++ b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php @@ -11,17 +11,13 @@ namespace Symfony\Bundle\FrameworkBundle; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConstraintValidatorsPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddAnnotationsCachedReaderPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddDebugLogProcessorPass; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddValidatorInitializersPass; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConsoleCommandPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CacheCollectorPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolClearerPass; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ControllerArgumentValueResolverPass; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FormPass; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\PropertyInfoPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\DataCollectorTranslatorPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TemplatingPass; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RoutingResolverPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ProfilerPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslatorPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\LoggingTranslatorPass; @@ -29,20 +25,29 @@ use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddCacheClearerPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddExpressionLanguageProvidersPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ContainerBuilderDebugDumpPass; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CompilerDebugDumpPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslationExtractorPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslationDumperPass; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\SerializerPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\UnusedTagsPass; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ConfigCachePass; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ValidateWorkflowsPass; +use Symfony\Component\Config\DependencyInjection\ConfigCachePass; +use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass; +use Symfony\Component\HttpKernel\DependencyInjection\ControllerArgumentValueResolverPass; +use Symfony\Component\HttpKernel\DependencyInjection\RegisterControllerArgumentLocatorsPass; +use Symfony\Component\HttpKernel\DependencyInjection\RemoveEmptyControllerArgumentLocatorsPass; +use Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass; +use Symfony\Component\Routing\DependencyInjection\RoutingResolverPass; +use Symfony\Component\Serializer\DependencyInjection\SerializerPass; use Symfony\Component\Debug\ErrorHandler; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass; use Symfony\Component\HttpKernel\DependencyInjection\FragmentRendererPass; +use Symfony\Component\Form\DependencyInjection\FormPass; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Bundle\Bundle; +use Symfony\Component\Config\Resource\ClassExistenceResource; +use Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass; +use Symfony\Component\Validator\DependencyInjection\AddValidatorInitializersPass; +use Symfony\Component\Workflow\DependencyInjection\ValidateWorkflowsPass; /** * Bundle. @@ -55,8 +60,12 @@ public function boot() { ErrorHandler::register(null, false)->throwAt($this->container->getParameter('debug.error_handler.throw_at'), true); - if ($trustedProxies = $this->container->getParameter('kernel.trusted_proxies')) { - Request::setTrustedProxies($trustedProxies); + if ($this->container->hasParameter('kernel.trusted_proxies')) { + @trigger_error('The "kernel.trusted_proxies" parameter is deprecated since version 3.3 and will be removed in 4.0. Use the Request::setTrustedProxies() method in your front controller instead.', E_USER_DEPRECATED); + + if ($trustedProxies = $this->container->getParameter('kernel.trusted_proxies')) { + Request::setTrustedProxies($trustedProxies, Request::getTrustedHeaderSet()); + } } if ($this->container->getParameter('kernel.http_method_override')) { @@ -72,16 +81,18 @@ public function build(ContainerBuilder $container) { parent::build($container); + $container->addCompilerPass(new RegisterControllerArgumentLocatorsPass()); + $container->addCompilerPass(new RemoveEmptyControllerArgumentLocatorsPass(), PassConfig::TYPE_BEFORE_REMOVING); $container->addCompilerPass(new RoutingResolverPass()); $container->addCompilerPass(new ProfilerPass()); // must be registered before removing private services as some might be listeners/subscribers // but as late as possible to get resolved parameters $container->addCompilerPass(new RegisterListenersPass(), PassConfig::TYPE_BEFORE_REMOVING); $container->addCompilerPass(new TemplatingPass()); - $container->addCompilerPass(new AddConstraintValidatorsPass(), PassConfig::TYPE_BEFORE_REMOVING); - $container->addCompilerPass(new AddValidatorInitializersPass()); - $container->addCompilerPass(new AddConsoleCommandPass()); - $container->addCompilerPass(new FormPass()); + $this->addCompilerPassIfExists($container, AddConstraintValidatorsPass::class, PassConfig::TYPE_BEFORE_REMOVING); + $container->addCompilerPass(new AddAnnotationsCachedReaderPass(), PassConfig::TYPE_BEFORE_REMOVING); + $this->addCompilerPassIfExists($container, AddValidatorInitializersPass::class); + $this->addCompilerPassIfExists($container, AddConsoleCommandPass::class); $container->addCompilerPass(new TranslatorPass()); $container->addCompilerPass(new LoggingTranslatorPass()); $container->addCompilerPass(new AddCacheWarmerPass()); @@ -90,19 +101,30 @@ public function build(ContainerBuilder $container) $container->addCompilerPass(new TranslationExtractorPass()); $container->addCompilerPass(new TranslationDumperPass()); $container->addCompilerPass(new FragmentRendererPass(), PassConfig::TYPE_AFTER_REMOVING); - $container->addCompilerPass(new SerializerPass()); - $container->addCompilerPass(new PropertyInfoPass()); + $this->addCompilerPassIfExists($container, SerializerPass::class); + $this->addCompilerPassIfExists($container, PropertyInfoPass::class); + $container->addCompilerPass(new DataCollectorTranslatorPass()); $container->addCompilerPass(new ControllerArgumentValueResolverPass()); $container->addCompilerPass(new CachePoolPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 32); - $container->addCompilerPass(new ValidateWorkflowsPass()); + $this->addCompilerPassIfExists($container, ValidateWorkflowsPass::class); $container->addCompilerPass(new CachePoolClearerPass(), PassConfig::TYPE_AFTER_REMOVING); + $this->addCompilerPassIfExists($container, FormPass::class); if ($container->getParameter('kernel.debug')) { $container->addCompilerPass(new AddDebugLogProcessorPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -32); $container->addCompilerPass(new UnusedTagsPass(), PassConfig::TYPE_AFTER_REMOVING); $container->addCompilerPass(new ContainerBuilderDebugDumpPass(), PassConfig::TYPE_AFTER_REMOVING); - $container->addCompilerPass(new CompilerDebugDumpPass(), PassConfig::TYPE_AFTER_REMOVING); - $container->addCompilerPass(new ConfigCachePass()); + $this->addCompilerPassIfExists($container, ConfigCachePass::class); + $container->addCompilerPass(new CacheCollectorPass()); + } + } + + private function addCompilerPassIfExists(ContainerBuilder $container, $class, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION, $priority = 0) + { + $container->addResource(new ClassExistenceResource($class)); + + if (class_exists($class)) { + $container->addCompilerPass(new $class(), $type, $priority); } } } diff --git a/src/Symfony/Bundle/FrameworkBundle/LICENSE b/src/Symfony/Bundle/FrameworkBundle/LICENSE index 12a74531e40a4..17d16a13367dd 100644 --- a/src/Symfony/Bundle/FrameworkBundle/LICENSE +++ b/src/Symfony/Bundle/FrameworkBundle/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2016 Fabien Potencier +Copyright (c) 2004-2017 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/annotations.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/annotations.xml index a2a0fb4065329..f3f4e0bd6114b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/annotations.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/annotations.xml @@ -6,12 +6,25 @@ - Doctrine\Common\Annotations\Reader + + required + + + + + class_exists + + + + + - + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/assets.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/assets.xml index 4f2e1fbf362a0..e83afa7cf4148 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/assets.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/assets.xml @@ -10,6 +10,7 @@ + @@ -21,13 +22,13 @@ - + - + @@ -38,5 +39,9 @@ + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.xml index 80cb00ada9652..72eca553f58af 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.xml @@ -22,13 +22,8 @@
- - - - - 0 - - %kernel.cache_dir%/pools + + @@ -93,11 +88,24 @@ + + + + + + 0 + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache_debug.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache_debug.xml new file mode 100644 index 0000000000000..3d68472028e93 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache_debug.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml index afebe6da98fd8..340c7e1972801 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml @@ -32,6 +32,7 @@ + %kernel.cache_dir%/%kernel.container_class% diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml new file mode 100644 index 0000000000000..350c2e20ef7a7 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml index 073851b038e67..3d260e4aff728 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml @@ -9,9 +9,9 @@ - + - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml index 51d374338e0b2..76c143b74e93e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml @@ -22,9 +22,11 @@ + %debug.file_link_format% + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml index d26c00cd50282..a9eb59b4782a4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml @@ -7,6 +7,7 @@ + @@ -21,26 +22,25 @@ + + - - - - - - - + + + + null - + @@ -61,7 +61,7 @@ - + @@ -71,7 +71,7 @@ The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - + @@ -158,7 +158,7 @@ - + @@ -173,20 +173,34 @@ - + - + - + - + %validator.translation_domain% + + + + + + + + + + + + + The service "%service_id%" is internal and deprecated since Symfony 3.3 and will be removed in Symfony 4.0 + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml index a562931a5e9a4..e4363184b95e4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml @@ -5,14 +5,22 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - + %form.type_extension.csrf.enabled% %form.type_extension.csrf.field_name% - + %validator.translation_domain% + + + + + + The service "%service_id%" is internal and deprecated since Symfony 3.3 and will be removed in Symfony 4.0 + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_debug.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_debug.xml index 843fc72bdb253..00e7c34e5e10d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_debug.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_debug.xml @@ -13,7 +13,7 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml index 3f0d319d9de13..963179c64e99e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml @@ -11,7 +11,7 @@ - + %kernel.debug% diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/identity_translator.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/identity_translator.xml new file mode 100644 index 0000000000000..542d6248db4d5 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/identity_translator.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml index d9e381c4806b8..af81e9dd0658b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml @@ -10,5 +10,6 @@ + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_info.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_info.xml index 33a0a661f8991..81e1037052b21 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_info.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_info.xml @@ -11,11 +11,12 @@ + - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_prod.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_prod.php deleted file mode 100644 index 97613a6248f71..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_prod.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -/* - * This file implements rewrite rules for PHP built-in web server. - * - * See: http://www.php.net/manual/en/features.commandline.webserver.php - * - * If you have custom directory layout, then you have to write your own router - * and pass it as a value to 'router' option of server:run command. - * - * @author: Michał Pipa - * @author: Albert Jessurum - */ - -// Workaround https://bugs.php.net/64566 -if (ini_get('auto_prepend_file') && !in_array(realpath(ini_get('auto_prepend_file')), get_included_files(), true)) { - require ini_get('auto_prepend_file'); -} - -if (is_file($_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR.$_SERVER['SCRIPT_NAME'])) { - return false; -} - -$_SERVER = array_merge($_SERVER, $_ENV); -$_SERVER['SCRIPT_FILENAME'] = $_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR.'app.php'; - -// Since we are rewriting to app.php, adjust SCRIPT_NAME and PHP_SELF accordingly -$_SERVER['SCRIPT_NAME'] = DIRECTORY_SEPARATOR.'app.php'; -$_SERVER['PHP_SELF'] = DIRECTORY_SEPARATOR.'app.php'; - -require 'app.php'; - -error_log(sprintf('%s:%d [%d]: %s', $_SERVER['REMOTE_ADDR'], $_SERVER['REMOTE_PORT'], http_response_code(), $_SERVER['REQUEST_URI']), 4); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_test.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_test.php deleted file mode 100644 index 5b020d5d22977..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_test.php +++ /dev/null @@ -1,31 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -/* - * This file implements rewrite rules for PHP built-in web server. - * - * See: http://www.php.net/manual/en/features.commandline.webserver.php - * - * If you have custom directory layout, then you have to write your own router - * and pass it as a value to 'router' option of server:run command. - * - * @author: Michał Pipa - * @author: Albert Jessurum - */ - -if (is_file($_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR.$_SERVER['SCRIPT_NAME'])) { - return false; -} - -$_SERVER = array_merge($_SERVER, $_ENV); -$_SERVER['SCRIPT_FILENAME'] = $_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR.'app_test.php'; - -require 'app_test.php'; diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml index 6050686d6c3c5..0befbe91c2a72 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml @@ -36,6 +36,11 @@ + + + + + @@ -75,6 +80,9 @@ + + + %router.request_context.base_url% @@ -84,6 +92,7 @@ %request_listener.http_port% %request_listener.https_port% + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd index fbf53309f41be..29d07c5758eed 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd @@ -14,6 +14,7 @@ + @@ -62,6 +63,10 @@ + + + + @@ -131,6 +136,7 @@ + @@ -143,6 +149,7 @@ + @@ -176,6 +183,7 @@ + @@ -184,6 +192,12 @@ + + + + + + @@ -197,6 +211,9 @@ + + + @@ -215,6 +232,7 @@ + @@ -231,13 +249,14 @@ - + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/security_csrf.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/security_csrf.xml index 354b515076968..489028d3f7b2b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/security_csrf.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/security_csrf.xml @@ -6,14 +6,17 @@ + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml index 5370d38072536..518164cf8a891 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml @@ -14,6 +14,11 @@ + + + + + @@ -27,6 +32,7 @@ + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml index f07f5261bb40a..86e3f597a1d27 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml @@ -8,6 +8,7 @@ + @@ -15,15 +16,16 @@ + + - Symfony\Component\HttpFoundation\ParameterBag Symfony\Component\HttpFoundation\HeaderBag @@ -31,35 +33,40 @@ Symfony\Component\HttpFoundation\ServerBag Symfony\Component\HttpFoundation\Request Symfony\Component\HttpKernel\Kernel - Symfony\Component\ClassLoader\ClassCollectionLoader - Symfony\Component\ClassLoader\ApcClassLoader + The "%service_id%" option is deprecated since version 3.3, to be removed in 4.0. - - Symfony\Component\DependencyInjection\ContainerInterface - Symfony\Component\DependencyInjection\Container - - + + %kernel.root_dir%/Resources + + %kernel.root_dir% + + %kernel.secret% - + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml index 2a4816a2b3bf1..55180b6027222 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml @@ -15,6 +15,10 @@ + + + + %session.metadata.storage_key% %session.metadata.update_threshold% @@ -47,9 +51,16 @@ - + - + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml index ebb311ceb0808..719c84aa1039a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml @@ -20,9 +20,16 @@ - - + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml index ba1c3bc168316..2fa53885b9d8f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml @@ -6,20 +6,19 @@ - + - + %kernel.default_locale% + %kernel.cache_dir%/translations %kernel.debug% - - - Symfony\Component\Translation\TranslatorInterface + @@ -27,12 +26,6 @@ - - - - - - diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml index 6da59e54e212e..2fb73fa39bbd6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml @@ -13,6 +13,7 @@ + @@ -57,8 +58,7 @@ - - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml index 72a200b313fa4..9bc44418cf52a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml @@ -21,7 +21,7 @@ - + @@ -32,6 +32,15 @@ + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web_link.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web_link.xml new file mode 100644 index 0000000000000..b80708defed13 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web_link.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/workflow.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/workflow.xml index 76592087a2260..0e79543ac6433 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/workflow.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/workflow.xml @@ -22,10 +22,13 @@ + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/attributes.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/attributes.html.php index eb421be817691..a9bd62f2a1b9e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/attributes.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/attributes.html.php @@ -1 +1,9 @@ -block($form, 'widget_attributes') ?> + $v): ?> + +escape($k), $view->escape(false !== $translation_domain ? $view['translator']->trans($v, array(), $translation_domain) : $v)) ?> + +escape($k), $view->escape($k)) ?> + +escape($k), $view->escape($v)) ?> + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_attributes.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_attributes.html.php index 2be960d0e179c..279233baa3fc0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_attributes.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_attributes.html.php @@ -1,10 +1,2 @@ -id="escape($id) ?>" name="escape($full_name) ?>" disabled="disabled" - $v): ?> - -escape($k), $view->escape(false !== $translation_domain ? $view['translator']->trans($v, array(), $translation_domain) : $v)) ?> - -escape($k), $view->escape($k)) ?> - -escape($k), $view->escape($v)) ?> - - +id="escape($id) ?>" name="escape($full_name) ?>" disabled="disabled" +block($form, 'attributes') : '' ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_label.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_label.html.php index 7a51b2ce7b4ff..78c953ec5a1f3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_label.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_label.html.php @@ -4,5 +4,5 @@ $name, '%id%' => $id)) : $view['form']->humanize($name); } ?> - +block($form, 'attributes', array('attr' => $label_attr)); } ?>>escape(false !== $translation_domain ? $view['translator']->trans($label, array(), $translation_domain) : $label) ?> 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 3fefa47c15c99..41c0cc7bfe8ba 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_attributes.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_attributes.html.php @@ -1,11 +1,3 @@ id="escape($id) ?>" name="escape($full_name) ?>" disabled="disabled" required="required" - $v): ?> - -escape($k), $view->escape(false !== $translation_domain ? $view['translator']->trans($v, array(), $translation_domain) : $v)) ?> - -escape($k), $view->escape($k)) ?> - -escape($k), $view->escape($v)) ?> - - +block($form, 'attributes') : '' ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_container_attributes.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_container_attributes.html.php index dc2e5ebea84e6..fdd176d12c79f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_container_attributes.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_container_attributes.html.php @@ -1,10 +1,2 @@ -id="escape($id) ?>" - $v): ?> - -escape($k), $view->escape(false !== $translation_domain ? $view['translator']->trans($v, array(), $translation_domain) : $v)) ?> - -escape($k), $view->escape($k)) ?> - -escape($k), $view->escape($v)) ?> - - +id="escape($id) ?>" +block($form, 'attributes') : '' ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php b/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php index 9783117f0b7f6..fce47e9eb8426 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php +++ b/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php @@ -64,7 +64,7 @@ public function load($resource, $type = null) // - this handles the case and prevents the second fatal error // by triggering an exception beforehand. - throw new FileLoaderLoadException($resource); + throw new FileLoaderLoadException($resource, null, null, null, $type); } $this->loading = true; diff --git a/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php b/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php index 35bd5ef1efed0..afc2ebe1a4e49 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php +++ b/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php @@ -11,6 +11,9 @@ namespace Symfony\Bundle\FrameworkBundle\Routing; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\Config\ContainerParametersResource; +use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; use Symfony\Component\Routing\Router as BaseRouter; use Symfony\Component\Routing\RequestContext; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -24,9 +27,10 @@ * * @author Fabien Potencier */ -class Router extends BaseRouter implements WarmableInterface +class Router extends BaseRouter implements WarmableInterface, ServiceSubscriberInterface { private $container; + private $collectedParameters = array(); /** * Constructor. @@ -53,6 +57,7 @@ public function getRouteCollection() if (null === $this->collection) { $this->collection = $this->container->get('routing.loader')->load($this->resource, $this->options['resource_type']); $this->resolveParameters($this->collection); + $this->collection->addResource(new ContainerParametersResource($this->collectedParameters)); } return $this->collection; @@ -153,6 +158,8 @@ private function resolve($value) $resolved = $container->getParameter($match[1]); if (is_string($resolved) || is_numeric($resolved)) { + $this->collectedParameters[$match[1]] = $resolved; + return (string) $resolved; } @@ -168,4 +175,14 @@ private function resolve($value) return str_replace('%%', '%', $escapedValue); } + + /** + * {@inheritdoc} + */ + public static function getSubscribedServices() + { + return array( + 'routing.loader' => LoaderInterface::class, + ); + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Templating/DelegatingEngine.php b/src/Symfony/Bundle/FrameworkBundle/Templating/DelegatingEngine.php index c204ffa2f03b2..1104cd73634c3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Templating/DelegatingEngine.php +++ b/src/Symfony/Bundle/FrameworkBundle/Templating/DelegatingEngine.php @@ -11,8 +11,8 @@ namespace Symfony\Bundle\FrameworkBundle\Templating; +use Psr\Container\ContainerInterface; use Symfony\Component\Templating\DelegatingEngine as BaseDelegatingEngine; -use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Response; /** diff --git a/src/Symfony/Bundle/FrameworkBundle/Templating/GlobalVariables.php b/src/Symfony/Bundle/FrameworkBundle/Templating/GlobalVariables.php index 606e886415c95..5ebfea1793784 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Templating/GlobalVariables.php +++ b/src/Symfony/Bundle/FrameworkBundle/Templating/GlobalVariables.php @@ -14,6 +14,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; /** * GlobalVariables is the entry point for Symfony global variables in PHP templates. @@ -33,21 +34,20 @@ public function __construct(ContainerInterface $container) } /** - * Returns the current user. - * - * @return mixed - * - * @see TokenInterface::getUser() + * @return TokenInterface|null */ - public function getUser() + public function getToken() { if (!$this->container->has('security.token_storage')) { return; } - $tokenStorage = $this->container->get('security.token_storage'); + return $this->container->get('security.token_storage')->getToken(); + } - if (!$token = $tokenStorage->getToken()) { + public function getUser() + { + if (!$token = $this->getToken()) { return; } @@ -60,8 +60,6 @@ public function getUser() } /** - * Returns the current request. - * * @return Request|null The HTTP request object */ public function getRequest() @@ -72,8 +70,6 @@ public function getRequest() } /** - * Returns the current session. - * * @return Session|null The session */ public function getSession() @@ -84,8 +80,6 @@ public function getSession() } /** - * Returns the current app environment. - * * @return string The current environment string (e.g 'dev') */ public function getEnvironment() @@ -94,8 +88,6 @@ public function getEnvironment() } /** - * Returns the current app debug mode. - * * @return bool The current debug mode */ public function getDebug() diff --git a/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/CodeHelper.php b/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/CodeHelper.php index c4085e4ec0f93..ba36cbde0c884 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/CodeHelper.php +++ b/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/CodeHelper.php @@ -133,7 +133,7 @@ public function fileExcerpt($file, $line) $code = @highlight_file($file, true); // remove main code/span tags $code = preg_replace('#^\s*(.*)\s*#s', '\\1', $code); - $content = preg_split('#
#', $code); + $content = explode('
', $code); $lines = array(); for ($i = max($line - 3, 1), $max = min($line + 3, count($content)); $i <= $max; ++$i) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Templating/Loader/TemplateLocator.php b/src/Symfony/Bundle/FrameworkBundle/Templating/Loader/TemplateLocator.php index bf6e795d80cc8..31fd9a368ff72 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Templating/Loader/TemplateLocator.php +++ b/src/Symfony/Bundle/FrameworkBundle/Templating/Loader/TemplateLocator.php @@ -24,6 +24,8 @@ class TemplateLocator implements FileLocatorInterface protected $locator; protected $cache; + private $cacheHits = array(); + /** * Constructor. * @@ -71,12 +73,15 @@ public function locate($template, $currentPath = null, $first = true) $key = $this->getCacheKey($template); + if (isset($this->cacheHits[$key])) { + return $this->cacheHits[$key]; + } if (isset($this->cache[$key])) { - return $this->cache[$key]; + return $this->cacheHits[$key] = realpath($this->cache[$key]) ?: $this->cache[$key]; } try { - return $this->cache[$key] = $this->locator->locate($template->getPath(), $currentPath); + return $this->cacheHits[$key] = $this->locator->locate($template->getPath(), $currentPath); } catch (\InvalidArgumentException $e) { throw new \InvalidArgumentException(sprintf('Unable to find template "%s" : "%s".', $template, $e->getMessage()), 0, $e); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Templating/PhpEngine.php b/src/Symfony/Bundle/FrameworkBundle/Templating/PhpEngine.php index 41382f769c65f..f9f5a5215fdfe 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Templating/PhpEngine.php +++ b/src/Symfony/Bundle/FrameworkBundle/Templating/PhpEngine.php @@ -11,10 +11,10 @@ namespace Symfony\Bundle\FrameworkBundle\Templating; +use Psr\Container\ContainerInterface; use Symfony\Component\Templating\PhpEngine as BasePhpEngine; use Symfony\Component\Templating\Loader\LoaderInterface; use Symfony\Component\Templating\TemplateNameParserInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Response; /** diff --git a/src/Symfony/Bundle/FrameworkBundle/Templating/TimedPhpEngine.php b/src/Symfony/Bundle/FrameworkBundle/Templating/TimedPhpEngine.php index 3295cc73a726f..6ab1b507ca264 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Templating/TimedPhpEngine.php +++ b/src/Symfony/Bundle/FrameworkBundle/Templating/TimedPhpEngine.php @@ -11,10 +11,10 @@ namespace Symfony\Bundle\FrameworkBundle\Templating; +use Psr\Container\ContainerInterface; use Symfony\Component\Templating\TemplateNameParserInterface; use Symfony\Component\Stopwatch\Stopwatch; use Symfony\Component\Templating\Loader\LoaderInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; /** * Times the time spent to render a template. diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php index 7f8de034f94b9..b8ec208c1dbe4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Test; +use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ResettableContainerInterface; use Symfony\Component\Finder\Finder; use Symfony\Component\HttpKernel\KernelInterface; @@ -20,7 +21,7 @@ * * @author Fabien Potencier */ -abstract class KernelTestCase extends \PHPUnit_Framework_TestCase +abstract class KernelTestCase extends TestCase { protected static $class; @@ -137,6 +138,8 @@ protected static function getKernelClass() * Boots the Kernel for this test. * * @param array $options + * + * @return KernelInterface A KernelInterface instance */ protected static function bootKernel(array $options = array()) { @@ -144,6 +147,8 @@ protected static function bootKernel(array $options = array()) static::$kernel = static::createKernel($options); static::$kernel->boot(); + + return static::$kernel; } /** diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/WebTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/WebTestCase.php index 5708ef716df7b..e62e2921a1875 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/WebTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/WebTestCase.php @@ -30,9 +30,9 @@ abstract class WebTestCase extends KernelTestCase */ protected static function createClient(array $options = array(), array $server = array()) { - static::bootKernel($options); + $kernel = static::bootKernel($options); - $client = static::$kernel->getContainer()->get('test.client'); + $client = $kernel->getContainer()->get('test.client'); $client->setServerParameters($server); return $client; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/ClassCacheCacheWarmerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/ClassCacheCacheWarmerTest.php index 889601b01fa6f..5e442d662f12a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/ClassCacheCacheWarmerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/ClassCacheCacheWarmerTest.php @@ -16,6 +16,9 @@ use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\WarmedClass; use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +/** + * @group legacy + */ class ClassCacheCacheWarmerTest extends TestCase { public function testWithDeclaredClasses() diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/TemplatePathsCacheWarmerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/TemplatePathsCacheWarmerTest.php index 062210d3a0585..74a33f3b43130 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/TemplatePathsCacheWarmerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/TemplatePathsCacheWarmerTest.php @@ -35,7 +35,7 @@ class TemplatePathsCacheWarmerTest extends TestCase private $tmpDir; - public function setUp() + protected function setUp() { $this->templateFinder = $this ->getMockBuilder(TemplateFinderInterface::class) @@ -56,7 +56,7 @@ public function setUp() $this->filesystem->mkdir($this->tmpDir); } - public function tearDown() + protected function tearDown() { $this->filesystem->remove($this->tmpDir); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/ValidatorCacheWarmerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/ValidatorCacheWarmerTest.php index 0e3fe47ce5ff8..23b4732afcb3a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/ValidatorCacheWarmerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/ValidatorCacheWarmerTest.php @@ -51,6 +51,39 @@ public function testWarmUp() $this->assertArrayHasKey('Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.Author', $values); } + public function testWarmUpWithAnnotations() + { + $validatorBuilder = new ValidatorBuilder(); + $validatorBuilder->addYamlMapping(__DIR__.'/../Fixtures/Validation/Resources/categories.yml'); + $validatorBuilder->enableAnnotationMapping(); + + $file = sys_get_temp_dir().'/cache-validator-with-annotations.php'; + @unlink($file); + + $fallbackPool = new ArrayAdapter(); + + $warmer = new ValidatorCacheWarmer($validatorBuilder, $file, $fallbackPool); + $warmer->warmUp(dirname($file)); + + $this->assertFileExists($file); + + $values = require $file; + + $this->assertInternalType('array', $values); + $this->assertCount(1, $values); + $this->assertArrayHasKey('Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.Category', $values); + + // Simple check to make sure that at least one constraint is actually cached, in this case the "id" property Type. + $this->assertContains('"int"', $values['Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.Category']); + + $values = $fallbackPool->getValues(); + + $this->assertInternalType('array', $values); + $this->assertCount(2, $values); + $this->assertArrayHasKey('Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.Category', $values); + $this->assertArrayHasKey('Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.SubCategory', $values); + } + public function testWarmUpWithoutLoader() { $validatorBuilder = new ValidatorBuilder(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php index ba969c64ae1f4..b9fe63ec5bbab 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php @@ -43,6 +43,9 @@ protected function tearDown() $this->fs->remove($this->rootDir); } + /** + * @group legacy + */ public function testCacheIsFreshAfterCacheClearedWithWarmup() { $input = new ArrayInput(array('cache:clear')); @@ -57,11 +60,10 @@ public function testCacheIsFreshAfterCacheClearedWithWarmup() // simply check that cache is warmed up $this->assertGreaterThanOrEqual(1, count($metaFiles)); $configCacheFactory = new ConfigCacheFactory(true); - $that = $this; foreach ($metaFiles as $file) { - $configCacheFactory->cache(substr($file, 0, -5), function () use ($that, $file) { - $that->fail(sprintf('Meta file "%s" is not fresh', (string) $file)); + $configCacheFactory->cache(substr($file, 0, -5), function () use ($file) { + $this->fail(sprintf('Meta file "%s" is not fresh', (string) $file)); }); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterDebugCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterDebugCommandTest.php index e42babd362404..a0cac9a908b24 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterDebugCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterDebugCommandTest.php @@ -11,13 +11,14 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Command; +use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Application; use Symfony\Component\Console\Tester\CommandTester; use Symfony\Bundle\FrameworkBundle\Command\RouterDebugCommand; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; -class RouterDebugCommandTest extends \PHPUnit_Framework_TestCase +class RouterDebugCommandTest extends TestCase { public function testDebugAllRoutes() { @@ -63,7 +64,7 @@ private function getContainer() { $routeCollection = new RouteCollection(); $routeCollection->add('foo', new Route('foo')); - $router = $this->getMock('Symfony\Component\Routing\RouterInterface'); + $router = $this->getMockBuilder('Symfony\Component\Routing\RouterInterface')->getMock(); $router ->expects($this->any()) ->method('getRouteCollection') @@ -74,7 +75,7 @@ private function getContainer() ->disableOriginalConstructor() ->getMock(); - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); $container ->expects($this->once()) ->method('has') diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterMatchCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterMatchCommandTest.php index 3dc64a300e8c6..db533c09742ba 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterMatchCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterMatchCommandTest.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Command; +use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Application; use Symfony\Component\Console\Tester\CommandTester; use Symfony\Bundle\FrameworkBundle\Command\RouterMatchCommand; @@ -19,7 +20,7 @@ use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RequestContext; -class RouterMatchCommandTest extends \PHPUnit_Framework_TestCase +class RouterMatchCommandTest extends TestCase { public function testWithMatchPath() { @@ -62,7 +63,7 @@ private function getContainer() $routeCollection = new RouteCollection(); $routeCollection->add('foo', new Route('foo')); $requestContext = new RequestContext(); - $router = $this->getMock('Symfony\Component\Routing\RouterInterface'); + $router = $this->getMockBuilder('Symfony\Component\Routing\RouterInterface')->getMock(); $router ->expects($this->any()) ->method('getRouteCollection') @@ -78,7 +79,7 @@ private function getContainer() ->disableOriginalConstructor() ->getMock(); - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); $container ->expects($this->once()) ->method('has') diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationDebugCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationDebugCommandTest.php index 27f61c4383f24..19c6d70156b19 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationDebugCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationDebugCommandTest.php @@ -11,12 +11,13 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Command; +use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Application; use Symfony\Component\Console\Tester\CommandTester; use Symfony\Bundle\FrameworkBundle\Command\TranslationDebugCommand; use Symfony\Component\Filesystem\Filesystem; -class TranslationDebugCommandTest extends \PHPUnit_Framework_TestCase +class TranslationDebugCommandTest extends TestCase { private $fs; private $translationDir; @@ -64,7 +65,7 @@ public function testDebugDefaultDirectory() public function testDebugCustomDirectory() { - $kernel = $this->getMock('Symfony\Component\HttpKernel\KernelInterface'); + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\KernelInterface')->getMock(); $kernel->expects($this->once()) ->method('getBundle') ->with($this->equalTo($this->translationDir)) @@ -82,7 +83,7 @@ public function testDebugCustomDirectory() */ public function testDebugInvalidDirectory() { - $kernel = $this->getMock('Symfony\Component\HttpKernel\KernelInterface'); + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\KernelInterface')->getMock(); $kernel->expects($this->once()) ->method('getBundle') ->with($this->equalTo('dir')) @@ -130,7 +131,7 @@ private function getContainer($extractedMessages = array(), $loadedMessages = ar ->method('getFallbackLocales') ->will($this->returnValue(array('en'))); - $extractor = $this->getMock('Symfony\Component\Translation\Extractor\ExtractorInterface'); + $extractor = $this->getMockBuilder('Symfony\Component\Translation\Extractor\ExtractorInterface')->getMock(); $extractor ->expects($this->any()) ->method('extract') @@ -140,7 +141,7 @@ private function getContainer($extractedMessages = array(), $loadedMessages = ar }) ); - $loader = $this->getMock('Symfony\Bundle\FrameworkBundle\Translation\TranslationLoader'); + $loader = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Translation\TranslationLoader')->getMock(); $loader ->expects($this->any()) ->method('loadMessages') @@ -151,7 +152,7 @@ private function getContainer($extractedMessages = array(), $loadedMessages = ar ); if (null === $kernel) { - $kernel = $this->getMock('Symfony\Component\HttpKernel\KernelInterface'); + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\KernelInterface')->getMock(); $kernel ->expects($this->any()) ->method('getBundle') @@ -166,7 +167,7 @@ private function getContainer($extractedMessages = array(), $loadedMessages = ar ->method('getRootDir') ->will($this->returnValue($this->translationDir)); - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); $container ->expects($this->any()) ->method('get') @@ -182,7 +183,7 @@ private function getContainer($extractedMessages = array(), $loadedMessages = ar private function getBundle($path) { - $bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\BundleInterface'); + $bundle = $this->getMockBuilder('Symfony\Component\HttpKernel\Bundle\BundleInterface')->getMock(); $bundle ->expects($this->any()) ->method('getPath') diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php index c876ee415de3c..e845619d9a826 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Command; +use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Application; use Symfony\Component\Console\Tester\CommandTester; use Symfony\Bundle\FrameworkBundle\Command\TranslationUpdateCommand; @@ -18,7 +19,7 @@ use Symfony\Component\DependencyInjection; use Symfony\Component\HttpKernel; -class TranslationUpdateCommandTest extends \PHPUnit_Framework_TestCase +class TranslationUpdateCommandTest extends TestCase { private $fs; private $translationDir; @@ -100,7 +101,7 @@ private function getContainer($extractedMessages = array(), $loadedMessages = ar ->method('getFallbackLocales') ->will($this->returnValue(array('en'))); - $extractor = $this->getMock('Symfony\Component\Translation\Extractor\ExtractorInterface'); + $extractor = $this->getMockBuilder('Symfony\Component\Translation\Extractor\ExtractorInterface')->getMock(); $extractor ->expects($this->any()) ->method('extract') @@ -112,7 +113,7 @@ private function getContainer($extractedMessages = array(), $loadedMessages = ar }) ); - $loader = $this->getMock('Symfony\Bundle\FrameworkBundle\Translation\TranslationLoader'); + $loader = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Translation\TranslationLoader')->getMock(); $loader ->expects($this->any()) ->method('loadMessages') @@ -122,7 +123,7 @@ private function getContainer($extractedMessages = array(), $loadedMessages = ar }) ); - $writer = $this->getMock('Symfony\Component\Translation\Writer\TranslationWriter'); + $writer = $this->getMockBuilder('Symfony\Component\Translation\Writer\TranslationWriter')->getMock(); $writer ->expects($this->any()) ->method('getFormats') @@ -131,7 +132,7 @@ private function getContainer($extractedMessages = array(), $loadedMessages = ar ); if (null === $kernel) { - $kernel = $this->getMock('Symfony\Component\HttpKernel\KernelInterface'); + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\KernelInterface')->getMock(); $kernel ->expects($this->any()) ->method('getBundle') @@ -146,7 +147,7 @@ private function getContainer($extractedMessages = array(), $loadedMessages = ar ->method('getRootDir') ->will($this->returnValue($this->translationDir)); - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); $container ->expects($this->any()) ->method('get') @@ -163,7 +164,7 @@ private function getContainer($extractedMessages = array(), $loadedMessages = ar private function getBundle($path) { - $bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\BundleInterface'); + $bundle = $this->getMockBuilder('Symfony\Component\HttpKernel\Bundle\BundleInterface')->getMock(); $bundle ->expects($this->any()) ->method('getPath') diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/YamlLintCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/YamlLintCommandTest.php index 30ffa76c23e81..de57b816eb4b8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/YamlLintCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/YamlLintCommandTest.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Command; +use PHPUnit\Framework\TestCase; use Symfony\Bundle\FrameworkBundle\Command\YamlLintCommand; use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Component\Console\Application as BaseApplication; @@ -25,7 +26,7 @@ * * @author Robin Chalas */ -class YamlLintCommandTest extends \PHPUnit_Framework_TestCase +class YamlLintCommandTest extends TestCase { private $files; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php index fc0e7654db2ca..fc60fd3bdd71c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php @@ -22,7 +22,7 @@ class ApplicationTest extends TestCase { public function testBundleInterfaceImplementation() { - $bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\BundleInterface'); + $bundle = $this->getMockBuilder('Symfony\Component\HttpKernel\Bundle\BundleInterface')->getMock(); $kernel = $this->getKernel(array($bundle), true); @@ -32,8 +32,7 @@ public function testBundleInterfaceImplementation() public function testBundleCommandsAreRegistered() { - $bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\Bundle'); - $bundle->expects($this->once())->method('registerCommands'); + $bundle = $this->createBundleMock(array()); $kernel = $this->getKernel(array($bundle), true); @@ -46,8 +45,7 @@ public function testBundleCommandsAreRegistered() public function testBundleCommandsAreRetrievable() { - $bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\Bundle'); - $bundle->expects($this->once())->method('registerCommands'); + $bundle = $this->createBundleMock(array()); $kernel = $this->getKernel(array($bundle)); @@ -60,47 +58,41 @@ public function testBundleCommandsAreRetrievable() public function testBundleSingleCommandIsRetrievable() { - $bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\Bundle'); - $bundle->expects($this->once())->method('registerCommands'); + $command = new Command('example'); + + $bundle = $this->createBundleMock(array($command)); $kernel = $this->getKernel(array($bundle)); $application = new Application($kernel); - $command = new Command('example'); - $application->add($command); - $this->assertSame($command, $application->get('example')); } public function testBundleCommandCanBeFound() { - $bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\Bundle'); - $bundle->expects($this->once())->method('registerCommands'); + $command = new Command('example'); + + $bundle = $this->createBundleMock(array($command)); $kernel = $this->getKernel(array($bundle)); $application = new Application($kernel); - $command = new Command('example'); - $application->add($command); - $this->assertSame($command, $application->find('example')); } public function testBundleCommandCanBeFoundByAlias() { - $bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\Bundle'); - $bundle->expects($this->once())->method('registerCommands'); + $command = new Command('example'); + $command->setAliases(array('alias')); + + $bundle = $this->createBundleMock(array($command)); $kernel = $this->getKernel(array($bundle)); $application = new Application($kernel); - $command = new Command('example'); - $command->setAliases(array('alias')); - $application->add($command); - $this->assertSame($command, $application->find('alias')); } @@ -125,10 +117,10 @@ public function testBundleCommandsHaveRightContainer() private function getKernel(array $bundles, $useDispatcher = false) { - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); if ($useDispatcher) { - $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock(); $dispatcher ->expects($this->atLeastOnce()) ->method('dispatch') @@ -153,7 +145,7 @@ private function getKernel(array $bundles, $useDispatcher = false) ->will($this->returnValue(array())) ; - $kernel = $this->getMock('Symfony\Component\HttpKernel\KernelInterface'); + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\KernelInterface')->getMock(); $kernel ->expects($this->any()) ->method('getBundles') @@ -167,4 +159,18 @@ private function getKernel(array $bundles, $useDispatcher = false) return $kernel; } + + private function createBundleMock(array $commands) + { + $bundle = $this->getMockBuilder('Symfony\Component\HttpKernel\Bundle\Bundle')->getMock(); + $bundle + ->expects($this->once()) + ->method('registerCommands') + ->will($this->returnCallback(function (Application $application) use ($commands) { + $application->addCommands($commands); + })) + ; + + return $bundle; + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php index 78a770da85a05..32b70a0829efb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor; +use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\BufferedOutput; use Symfony\Component\Console\Style\SymfonyStyle; @@ -22,7 +23,7 @@ use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; -abstract class AbstractDescriptorTest extends \PHPUnit_Framework_TestCase +abstract class AbstractDescriptorTest extends TestCase { /** @dataProvider getDescribeRouteCollectionTestData */ public function testDescribeRouteCollection(RouteCollection $routes, $expectedDescription) @@ -79,6 +80,24 @@ public function getDescribeContainerDefinitionTestData() return $this->getDescriptionTestData(ObjectsProvider::getContainerDefinitions()); } + /** @dataProvider getDescribeContainerDefinitionWithArgumentsShownTestData */ + public function testDescribeContainerDefinitionWithArgumentsShown(Definition $definition, $expectedDescription) + { + $this->assertDescription($expectedDescription, $definition, array('show_arguments' => true)); + } + + public function getDescribeContainerDefinitionWithArgumentsShownTestData() + { + $definitions = ObjectsProvider::getContainerDefinitions(); + $definitionsWithArgs = array(); + + foreach ($definitions as $key => $definition) { + $definitionsWithArgs[str_replace('definition_', 'definition_arguments_', $key)] = $definition; + } + + return $this->getDescriptionTestData($definitionsWithArgs); + } + /** @dataProvider getDescribeContainerAliasTestData */ public function testDescribeContainerAlias(Alias $alias, $expectedDescription) { @@ -90,6 +109,35 @@ public function getDescribeContainerAliasTestData() return $this->getDescriptionTestData(ObjectsProvider::getContainerAliases()); } + /** @dataProvider getDescribeContainerDefinitionWhichIsAnAliasTestData */ + public function testDescribeContainerDefinitionWhichIsAnAlias(Alias $alias, $expectedDescription, ContainerBuilder $builder, $options = array()) + { + $this->assertDescription($expectedDescription, $builder, $options); + } + + public function getDescribeContainerDefinitionWhichIsAnAliasTestData() + { + $builder = current(ObjectsProvider::getContainerBuilders()); + $builder->setDefinition('service_1', $builder->getDefinition('definition_1')); + $builder->setDefinition('service_2', $builder->getDefinition('definition_2')); + + $aliases = ObjectsProvider::getContainerAliases(); + $aliasesWithDefinitions = array(); + foreach ($aliases as $name => $alias) { + $aliasesWithDefinitions[str_replace('alias_', 'alias_with_definition_', $name)] = $alias; + } + + $i = 0; + $data = $this->getDescriptionTestData($aliasesWithDefinitions); + foreach ($aliases as $name => $alias) { + $data[$i][] = $builder; + $data[$i][] = array('id' => $name); + ++$i; + } + + return $data; + } + /** @dataProvider getDescribeContainerParameterTestData */ public function testDescribeContainerParameter($parameter, $expectedDescription, array $options) { @@ -144,7 +192,7 @@ private function assertDescription($expectedDescription, $describedObject, array $this->getDescriptor()->describe($output, $describedObject, $options); if ('json' === $this->getFormat()) { - $this->assertEquals(json_decode($expectedDescription), json_decode($output->fetch())); + $this->assertEquals(json_encode(json_decode($expectedDescription), JSON_PRETTY_PRINT), json_encode(json_decode($output->fetch()), JSON_PRETTY_PRINT)); } else { $this->assertEquals(trim($expectedDescription), trim(str_replace(PHP_EOL, "\n", $output->fetch()))); } @@ -168,6 +216,7 @@ private function getContainerBuilderDescriptionTestData(array $objects) 'public' => array('show_private' => false), 'tag1' => array('show_private' => true, 'tag' => 'tag1'), 'tags' => array('group_by' => 'tags', 'show_private' => true), + 'arguments' => array('show_private' => false, 'show_arguments' => true), ); $data = array(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/ObjectsProvider.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/ObjectsProvider.php index 5244072ae85cd..0c0363c482bab 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/ObjectsProvider.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/ObjectsProvider.php @@ -12,6 +12,8 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor; use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; @@ -105,6 +107,19 @@ public static function getContainerDefinitions() ->setSynthetic(false) ->setLazy(true) ->setAbstract(true) + ->addArgument(new Reference('definition2')) + ->addArgument('%parameter%') + ->addArgument(new Definition('inline_service', array('arg1', 'arg2'))) + ->addArgument(array( + 'foo', + new Reference('definition2'), + new Definition('inline_service'), + )) + ->addArgument(new IteratorArgument(array( + new Reference('definition_1'), + new Reference('definition_2'), + ))) + ->addArgument(new ClosureProxyArgument('definition1', 'get')) ->setFactory(array('Full\\Qualified\\FactoryClass', 'get')), 'definition_2' => $definition2 ->setPublic(false) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/TextDescriptorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/TextDescriptorTest.php index ce4f377c508fd..e775ac7cf199a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/TextDescriptorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/TextDescriptorTest.php @@ -15,6 +15,16 @@ class TextDescriptorTest extends AbstractDescriptorTest { + protected function setUp() + { + putenv('COLUMNS=121'); + } + + protected function tearDown() + { + putenv('COLUMNS'); + } + protected function getDescriptor() { return new TextDescriptor(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/AbstractControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/AbstractControllerTest.php new file mode 100644 index 0000000000000..937cbfc7286d0 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/AbstractControllerTest.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Controller; + +use Psr\Container\ContainerInterface; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\File\File; + +class AbstractControllerTest extends ControllerTraitTest +{ + protected function createController() + { + return new TestAbstractController(); + } +} + +class TestAbstractController extends AbstractController +{ + use TestControllerTrait; + + private $throwOnUnexpectedService; + + public function __construct($throwOnUnexpectedService = true) + { + $this->throwOnUnexpectedService = $throwOnUnexpectedService; + } + + public function setContainer(ContainerInterface $container) + { + if (!$this->throwOnUnexpectedService) { + return parent::setContainer($container); + } + + $expected = self::getSubscribedServices(); + + foreach ($container->getServiceIds() as $id) { + if ('service_container' === $id) { + continue; + } + if (!isset($expected[$id])) { + throw new \UnexpectedValueException(sprintf('Service "%s" is not expected, as declared by %s::getSubscribedServices()', $id, AbstractController::class)); + } + $type = substr($expected[$id], 1); + if (!$container->get($id) instanceof $type) { + throw new \UnexpectedValueException(sprintf('Service "%s" is expected to be an instance of "%s", as declared by %s::getSubscribedServices()', $id, $type, AbstractController::class)); + } + } + + return parent::setContainer($container); + } + + public function fooAction() + { + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerNameParserTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerNameParserTest.php index 894e7ae3285d3..57eaf269f4424 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerNameParserTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerNameParserTest.php @@ -11,9 +11,9 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Controller; +use Composer\Autoload\ClassLoader; use Symfony\Bundle\FrameworkBundle\Tests\TestCase; use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser; -use Symfony\Component\ClassLoader\ClassLoader; class ControllerNameParserTest extends TestCase { @@ -22,17 +22,14 @@ class ControllerNameParserTest extends TestCase protected function setUp() { $this->loader = new ClassLoader(); - $this->loader->addPrefixes(array( - 'TestBundle' => __DIR__.'/../Fixtures', - 'TestApplication' => __DIR__.'/../Fixtures', - )); + $this->loader->add('TestBundle', __DIR__.'/../Fixtures'); + $this->loader->add('TestApplication', __DIR__.'/../Fixtures'); $this->loader->register(); } protected function tearDown() { - spl_autoload_unregister(array($this->loader, 'loadClass')); - + $this->loader->unregister(); $this->loader = null; } @@ -147,7 +144,7 @@ private function createParser() 'FabpotFooBundle' => array($this->getBundle('TestBundle\Fabpot\FooBundle', 'FabpotFooBundle'), $this->getBundle('TestBundle\Sensio\FooBundle', 'SensioFooBundle')), ); - $kernel = $this->getMock('Symfony\Component\HttpKernel\KernelInterface'); + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\KernelInterface')->getMock(); $kernel ->expects($this->any()) ->method('getBundle') @@ -178,7 +175,7 @@ private function createParser() private function getBundle($namespace, $name) { - $bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\BundleInterface'); + $bundle = $this->getMockBuilder('Symfony\Component\HttpKernel\Bundle\BundleInterface')->getMock(); $bundle->expects($this->any())->method('getName')->will($this->returnValue($name)); $bundle->expects($this->any())->method('getNamespace')->will($this->returnValue($namespace)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php index b511f90d17adb..7946a96e8d2c0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php @@ -11,15 +11,17 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Controller; +use Psr\Container\ContainerInterface as Psr11ContainerInterface; use Psr\Log\LoggerInterface; use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser; use Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver; +use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest as BaseControllerResolverTest; +use Symfony\Component\HttpKernel\Tests\Controller\ContainerControllerResolverTest; -class ControllerResolverTest extends BaseControllerResolverTest +class ControllerResolverTest extends ContainerControllerResolverTest { public function testGetControllerOnContainerAware() { @@ -55,7 +57,7 @@ public function testGetControllerWithBundleNotation() ->will($this->returnValue('Symfony\Bundle\FrameworkBundle\Tests\Controller\ContainerAwareController::testAction')) ; - $resolver = $this->createControllerResolver(null, $parser); + $resolver = $this->createControllerResolver(null, null, $parser); $request = Request::create('/'); $request->attributes->set('_controller', $shortName); @@ -66,105 +68,45 @@ public function testGetControllerWithBundleNotation() $this->assertSame('testAction', $controller[1]); } - public function testGetControllerService() + public function testAbstractControllerGetsContainerWhenNotSet() { - $container = $this->createMockContainer(); - $container->expects($this->once()) - ->method('get') - ->with('foo') - ->will($this->returnValue($this)) - ; - - $resolver = $this->createControllerResolver(null, null, $container); - $request = Request::create('/'); - $request->attributes->set('_controller', 'foo:controllerMethod1'); + class_exists(AbstractControllerTest::class); - $controller = $resolver->getController($request); + $controller = new TestAbstractController(false); - $this->assertInstanceOf(get_class($this), $controller[0]); - $this->assertSame('controllerMethod1', $controller[1]); - } + $container = new Container(); + $container->set(TestAbstractController::class, $controller); - public function testGetControllerInvokableService() - { - $invokableController = new InvokableController('bar'); + $resolver = $this->createControllerResolver(null, $container); - $container = $this->createMockContainer(); - $container->expects($this->once()) - ->method('has') - ->with('foo') - ->will($this->returnValue(true)) - ; - $container->expects($this->once()) - ->method('get') - ->with('foo') - ->will($this->returnValue($invokableController)) - ; - - $resolver = $this->createControllerResolver(null, null, $container); $request = Request::create('/'); - $request->attributes->set('_controller', 'foo'); - - $controller = $resolver->getController($request); + $request->attributes->set('_controller', TestAbstractController::class.'::fooAction'); - $this->assertEquals($invokableController, $controller); + $this->assertSame(array($controller, 'fooAction'), $resolver->getController($request)); + $this->assertSame($container, $controller->setContainer($container)); } - public function testGetControllerInvokableServiceWithClassNameAsName() + public function testAbstractControllerGetsNoContainerWhenSet() { - $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); + class_exists(AbstractControllerTest::class); - $controller = $resolver->getController($request); + $controller = new TestAbstractController(false); + $controllerContainer = new Container(); + $controller->setContainer($controllerContainer); - $this->assertEquals($invokableController, $controller); - } + $container = new Container(); + $container->set(TestAbstractController::class, $controller); - /** - * @dataProvider getUndefinedControllers - */ - public function testGetControllerOnNonUndefinedFunction($controller, $exceptionName = null, $exceptionMessage = null) - { - // All this logic needs to be duplicated, since calling parent::testGetControllerOnNonUndefinedFunction will override the expected excetion and not use the regex - $resolver = $this->createControllerResolver(); - $this->setExpectedExceptionRegExp($exceptionName, $exceptionMessage); + $resolver = $this->createControllerResolver(null, $container); $request = Request::create('/'); - $request->attributes->set('_controller', $controller); - $resolver->getController($request); - } + $request->attributes->set('_controller', TestAbstractController::class.'::fooAction'); - public function getUndefinedControllers() - { - return array( - array('foo', '\LogicException', '/Unable to parse the controller name "foo"\./'), - array('oof::bar', '\InvalidArgumentException', '/Class "oof" does not exist\./'), - array('stdClass', '\LogicException', '/Unable to parse the controller name "stdClass"\./'), - array( - 'Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest::bar', - '\InvalidArgumentException', - '/.?[cC]ontroller(.*?) for URI "\/" is not callable\.( Expected method(.*) Available methods)?/', - ), - ); + $this->assertSame(array($controller, 'fooAction'), $resolver->getController($request)); + $this->assertSame($controllerContainer, $controller->setContainer($container)); } - protected function createControllerResolver(LoggerInterface $logger = null, ControllerNameParser $parser = null, ContainerInterface $container = null) + protected function createControllerResolver(LoggerInterface $logger = null, Psr11ContainerInterface $container = null, ControllerNameParser $parser = null) { if (!$parser) { $parser = $this->createMockParser(); @@ -179,12 +121,12 @@ protected function createControllerResolver(LoggerInterface $logger = null, Cont protected function createMockParser() { - return $this->getMock('Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser', array(), array(), '', false); + return $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser')->disableOriginalConstructor()->getMock(); } protected function createMockContainer() { - return $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + return $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); } } @@ -210,14 +152,3 @@ public function __invoke() { } } - -class InvokableController -{ - public function __construct($bar) // mandatory argument to prevent automatic instantiation - { - } - - public function __invoke() - { - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTest.php index 2821e7faf837e..9fd20649289de 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTest.php @@ -11,679 +11,18 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Controller; -use Symfony\Bundle\FrameworkBundle\Tests\TestCase; use Symfony\Bundle\FrameworkBundle\Controller\Controller; -use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\File\File; -use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpFoundation\ResponseHeaderBag; -use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; -use Symfony\Component\HttpFoundation\StreamedResponse; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; -use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; -use Symfony\Component\Security\Core\User\User; -use Symfony\Component\Serializer\SerializerInterface; -class ControllerTest extends TestCase +class ControllerTest extends ControllerTraitTest { - public function testForward() + protected function createController() { - $request = Request::create('/'); - $request->setLocale('fr'); - $request->setRequestFormat('xml'); - - $requestStack = new RequestStack(); - $requestStack->push($request); - - $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); - $kernel->expects($this->once())->method('handle')->will($this->returnCallback(function (Request $request) { - return new Response($request->getRequestFormat().'--'.$request->getLocale()); - })); - - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $container->expects($this->at(0))->method('get')->will($this->returnValue($requestStack)); - $container->expects($this->at(1))->method('get')->will($this->returnValue($kernel)); - - $controller = new TestController(); - $controller->setContainer($container); - - $response = $controller->forward('a_controller'); - $this->assertEquals('xml--fr', $response->getContent()); - } - - public function testGetUser() - { - $user = new User('user', 'pass'); - $token = new UsernamePasswordToken($user, 'pass', 'default', array('ROLE_USER')); - - $controller = new TestController(); - $controller->setContainer($this->getContainerWithTokenStorage($token)); - - $this->assertSame($controller->getUser(), $user); - } - - public function testGetUserAnonymousUserConvertedToNull() - { - $token = new AnonymousToken('default', 'anon.'); - - $controller = new TestController(); - $controller->setContainer($this->getContainerWithTokenStorage($token)); - - $this->assertNull($controller->getUser()); - } - - public function testGetUserWithEmptyTokenStorage() - { - $controller = new TestController(); - $controller->setContainer($this->getContainerWithTokenStorage(null)); - - $this->assertNull($controller->getUser()); - } - - /** - * @expectedException \LogicException - * @expectedExceptionMessage The SecurityBundle is not registered in your application. - */ - public function testGetUserWithEmptyContainer() - { - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $container - ->expects($this->once()) - ->method('has') - ->with('security.token_storage') - ->will($this->returnValue(false)); - - $controller = new TestController(); - $controller->setContainer($container); - - $controller->getUser(); - } - - /** - * @param $token - * - * @return ContainerInterface - */ - private function getContainerWithTokenStorage($token = null) - { - $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage'); - $tokenStorage - ->expects($this->once()) - ->method('getToken') - ->will($this->returnValue($token)); - - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $container - ->expects($this->once()) - ->method('has') - ->with('security.token_storage') - ->will($this->returnValue(true)); - - $container - ->expects($this->once()) - ->method('get') - ->with('security.token_storage') - ->will($this->returnValue($tokenStorage)); - - return $container; - } - - public function testJson() - { - $container = $this->getMock(ContainerInterface::class); - $container - ->expects($this->once()) - ->method('has') - ->with('serializer') - ->will($this->returnValue(false)); - - $controller = new TestController(); - $controller->setContainer($container); - - $response = $controller->json(array()); - $this->assertInstanceOf(JsonResponse::class, $response); - $this->assertEquals('[]', $response->getContent()); - } - - public function testJsonWithSerializer() - { - $container = $this->getMock(ContainerInterface::class); - $container - ->expects($this->once()) - ->method('has') - ->with('serializer') - ->will($this->returnValue(true)); - - $serializer = $this->getMock(SerializerInterface::class); - $serializer - ->expects($this->once()) - ->method('serialize') - ->with(array(), 'json', array('json_encode_options' => JsonResponse::DEFAULT_ENCODING_OPTIONS)) - ->will($this->returnValue('[]')); - - $container - ->expects($this->once()) - ->method('get') - ->with('serializer') - ->will($this->returnValue($serializer)); - - $controller = new TestController(); - $controller->setContainer($container); - - $response = $controller->json(array()); - $this->assertInstanceOf(JsonResponse::class, $response); - $this->assertEquals('[]', $response->getContent()); - } - - public function testJsonWithSerializerContextOverride() - { - $container = $this->getMock(ContainerInterface::class); - $container - ->expects($this->once()) - ->method('has') - ->with('serializer') - ->will($this->returnValue(true)); - - $serializer = $this->getMock(SerializerInterface::class); - $serializer - ->expects($this->once()) - ->method('serialize') - ->with(array(), 'json', array('json_encode_options' => 0, 'other' => 'context')) - ->will($this->returnValue('[]')); - - $container - ->expects($this->once()) - ->method('get') - ->with('serializer') - ->will($this->returnValue($serializer)); - - $controller = new TestController(); - $controller->setContainer($container); - - $response = $controller->json(array(), 200, array(), array('json_encode_options' => 0, 'other' => 'context')); - $this->assertInstanceOf(JsonResponse::class, $response); - $this->assertEquals('[]', $response->getContent()); - $response->setEncodingOptions(JSON_FORCE_OBJECT); - $this->assertEquals('{}', $response->getContent()); - } - - public function testFile() - { - /* @var ContainerInterface $container */ - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); - $container->set('kernel', $kernel); - - $controller = new TestController(); - $controller->setContainer($container); - - /* @var BinaryFileResponse $response */ - $response = $controller->file(new File(__FILE__)); - $this->assertInstanceOf(BinaryFileResponse::class, $response); - $this->assertSame(200, $response->getStatusCode()); - if ($response->headers->get('content-type')) { - $this->assertSame('text/x-php', $response->headers->get('content-type')); - } - $this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition')); - $this->assertContains(basename(__FILE__), $response->headers->get('content-disposition')); - } - - public function testFileAsInline() - { - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $controller = new TestController(); - $controller->setContainer($container); - - /* @var BinaryFileResponse $response */ - $response = $controller->file(new File(__FILE__), null, ResponseHeaderBag::DISPOSITION_INLINE); - - $this->assertInstanceOf(BinaryFileResponse::class, $response); - $this->assertSame(200, $response->getStatusCode()); - if ($response->headers->get('content-type')) { - $this->assertSame('text/x-php', $response->headers->get('content-type')); - } - $this->assertContains(ResponseHeaderBag::DISPOSITION_INLINE, $response->headers->get('content-disposition')); - $this->assertContains(basename(__FILE__), $response->headers->get('content-disposition')); - } - - public function testFileWithOwnFileName() - { - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $controller = new TestController(); - $controller->setContainer($container); - - /* @var BinaryFileResponse $response */ - $fileName = 'test.php'; - $response = $controller->file(new File(__FILE__), $fileName); - - $this->assertInstanceOf(BinaryFileResponse::class, $response); - $this->assertSame(200, $response->getStatusCode()); - if ($response->headers->get('content-type')) { - $this->assertSame('text/x-php', $response->headers->get('content-type')); - } - $this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition')); - $this->assertContains($fileName, $response->headers->get('content-disposition')); - } - - public function testFileWithOwnFileNameAsInline() - { - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $controller = new TestController(); - $controller->setContainer($container); - - /* @var BinaryFileResponse $response */ - $fileName = 'test.php'; - $response = $controller->file(new File(__FILE__), $fileName, ResponseHeaderBag::DISPOSITION_INLINE); - - $this->assertInstanceOf(BinaryFileResponse::class, $response); - $this->assertSame(200, $response->getStatusCode()); - if ($response->headers->get('content-type')) { - $this->assertSame('text/x-php', $response->headers->get('content-type')); - } - $this->assertContains(ResponseHeaderBag::DISPOSITION_INLINE, $response->headers->get('content-disposition')); - $this->assertContains($fileName, $response->headers->get('content-disposition')); - } - - public function testFileFromPath() - { - $controller = new TestController(); - - /* @var BinaryFileResponse $response */ - $response = $controller->file(__FILE__); - - $this->assertInstanceOf(BinaryFileResponse::class, $response); - $this->assertSame(200, $response->getStatusCode()); - if ($response->headers->get('content-type')) { - $this->assertSame('text/x-php', $response->headers->get('content-type')); - } - $this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition')); - $this->assertContains(basename(__FILE__), $response->headers->get('content-disposition')); - } - - public function testFileFromPathWithCustomizedFileName() - { - $controller = new TestController(); - - /* @var BinaryFileResponse $response */ - $response = $controller->file(__FILE__, 'test.php'); - - $this->assertInstanceOf(BinaryFileResponse::class, $response); - $this->assertSame(200, $response->getStatusCode()); - if ($response->headers->get('content-type')) { - $this->assertSame('text/x-php', $response->headers->get('content-type')); - } - $this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition')); - $this->assertContains('test.php', $response->headers->get('content-disposition')); - } - - /** - * @expectedException \Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException - */ - public function testFileWhichDoesNotExist() - { - $controller = new TestController(); - - /* @var BinaryFileResponse $response */ - $response = $controller->file('some-file.txt', 'test.php'); - } - - public function testIsGranted() - { - $authorizationChecker = $this->getMock('Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface'); - $authorizationChecker->expects($this->once())->method('isGranted')->willReturn(true); - - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $container->expects($this->at(0))->method('has')->will($this->returnValue(true)); - $container->expects($this->at(1))->method('get')->will($this->returnValue($authorizationChecker)); - - $controller = new TestController(); - $controller->setContainer($container); - - $this->assertTrue($controller->isGranted('foo')); - } - - /** - * @expectedException \Symfony\Component\Security\Core\Exception\AccessDeniedException - */ - public function testdenyAccessUnlessGranted() - { - $authorizationChecker = $this->getMock('Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface'); - $authorizationChecker->expects($this->once())->method('isGranted')->willReturn(false); - - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $container->expects($this->at(0))->method('has')->will($this->returnValue(true)); - $container->expects($this->at(1))->method('get')->will($this->returnValue($authorizationChecker)); - - $controller = new TestController(); - $controller->setContainer($container); - - $controller->denyAccessUnlessGranted('foo'); - } - - public function testRenderViewTwig() - { - $twig = $this->getMockBuilder('\Twig_Environment')->disableOriginalConstructor()->getMock(); - $twig->expects($this->once())->method('render')->willReturn('bar'); - - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $container->expects($this->at(0))->method('has')->will($this->returnValue(false)); - $container->expects($this->at(1))->method('has')->will($this->returnValue(true)); - $container->expects($this->at(2))->method('get')->will($this->returnValue($twig)); - - $controller = new TestController(); - $controller->setContainer($container); - - $this->assertEquals('bar', $controller->renderView('foo')); - } - - public function testRenderTwig() - { - $twig = $this->getMockBuilder('\Twig_Environment')->disableOriginalConstructor()->getMock(); - $twig->expects($this->once())->method('render')->willReturn('bar'); - - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $container->expects($this->at(0))->method('has')->will($this->returnValue(false)); - $container->expects($this->at(1))->method('has')->will($this->returnValue(true)); - $container->expects($this->at(2))->method('get')->will($this->returnValue($twig)); - - $controller = new TestController(); - $controller->setContainer($container); - - $this->assertEquals('bar', $controller->render('foo')->getContent()); - } - - public function testStreamTwig() - { - $twig = $this->getMockBuilder('\Twig_Environment')->disableOriginalConstructor()->getMock(); - - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $container->expects($this->at(0))->method('has')->will($this->returnValue(false)); - $container->expects($this->at(1))->method('has')->will($this->returnValue(true)); - $container->expects($this->at(2))->method('get')->will($this->returnValue($twig)); - - $controller = new TestController(); - $controller->setContainer($container); - - $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $controller->stream('foo')); - } - - public function testRedirectToRoute() - { - $router = $this->getMock('Symfony\Component\Routing\RouterInterface'); - $router->expects($this->once())->method('generate')->willReturn('/foo'); - - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $container->expects($this->at(0))->method('get')->will($this->returnValue($router)); - - $controller = new TestController(); - $controller->setContainer($container); - $response = $controller->redirectToRoute('foo'); - - $this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $response); - $this->assertSame('/foo', $response->getTargetUrl()); - $this->assertSame(302, $response->getStatusCode()); - } - - public function testAddFlash() - { - $flashBag = new FlashBag(); - $session = $this->getMock('Symfony\Component\HttpFoundation\Session\Session'); - $session->expects($this->once())->method('getFlashBag')->willReturn($flashBag); - - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $container->expects($this->at(0))->method('has')->will($this->returnValue(true)); - $container->expects($this->at(1))->method('get')->will($this->returnValue($session)); - - $controller = new TestController(); - $controller->setContainer($container); - $controller->addFlash('foo', 'bar'); - - $this->assertSame(array('bar'), $flashBag->get('foo')); - } - - public function testCreateAccessDeniedException() - { - $controller = new TestController(); - - $this->assertInstanceOf('Symfony\Component\Security\Core\Exception\AccessDeniedException', $controller->createAccessDeniedException()); - } - - public function testIsCsrfTokenValid() - { - $tokenManager = $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface'); - $tokenManager->expects($this->once())->method('isTokenValid')->willReturn(true); - - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $container->expects($this->at(0))->method('has')->will($this->returnValue(true)); - $container->expects($this->at(1))->method('get')->will($this->returnValue($tokenManager)); - - $controller = new TestController(); - $controller->setContainer($container); - - $this->assertTrue($controller->isCsrfTokenValid('foo', 'bar')); - } - - public function testGenerateUrl() - { - $router = $this->getMock('Symfony\Component\Routing\RouterInterface'); - $router->expects($this->once())->method('generate')->willReturn('/foo'); - - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $container->expects($this->at(0))->method('get')->will($this->returnValue($router)); - - $controller = new TestController(); - $controller->setContainer($container); - - $this->assertEquals('/foo', $controller->generateUrl('foo')); - } - - public function testRedirect() - { - $controller = new TestController(); - $response = $controller->redirect('http://dunglas.fr', 301); - - $this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $response); - $this->assertSame('http://dunglas.fr', $response->getTargetUrl()); - $this->assertSame(301, $response->getStatusCode()); - } - - public function testRenderViewTemplating() - { - $templating = $this->getMock('Symfony\Bundle\FrameworkBundle\Templating\EngineInterface'); - $templating->expects($this->once())->method('render')->willReturn('bar'); - - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $container->expects($this->at(0))->method('has')->willReturn(true); - $container->expects($this->at(1))->method('get')->will($this->returnValue($templating)); - - $controller = new TestController(); - $controller->setContainer($container); - - $this->assertEquals('bar', $controller->renderView('foo')); - } - - public function testRenderTemplating() - { - $templating = $this->getMock('Symfony\Bundle\FrameworkBundle\Templating\EngineInterface'); - $templating->expects($this->once())->method('renderResponse')->willReturn(new Response('bar')); - - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $container->expects($this->at(0))->method('has')->willReturn(true); - $container->expects($this->at(1))->method('get')->will($this->returnValue($templating)); - - $controller = new TestController(); - $controller->setContainer($container); - - $this->assertEquals('bar', $controller->render('foo')->getContent()); - } - - public function testStreamTemplating() - { - $templating = $this->getMock('Symfony\Component\Routing\RouterInterface'); - - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $container->expects($this->at(0))->method('has')->willReturn(true); - $container->expects($this->at(1))->method('get')->will($this->returnValue($templating)); - - $controller = new TestController(); - $controller->setContainer($container); - - $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $controller->stream('foo')); - } - - public function testCreateNotFoundException() - { - $controller = new TestController(); - - $this->assertInstanceOf('Symfony\Component\HttpKernel\Exception\NotFoundHttpException', $controller->createNotFoundException()); - } - - public function testCreateForm() - { - $form = $this->getMock('Symfony\Component\Form\FormInterface'); - - $formFactory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); - $formFactory->expects($this->once())->method('create')->willReturn($form); - - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $container->expects($this->at(0))->method('get')->will($this->returnValue($formFactory)); - - $controller = new TestController(); - $controller->setContainer($container); - - $this->assertEquals($form, $controller->createForm('foo')); - } - - public function testCreateFormBuilder() - { - $formBuilder = $this->getMock('Symfony\Component\Form\FormBuilderInterface'); - - $formFactory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); - $formFactory->expects($this->once())->method('createBuilder')->willReturn($formBuilder); - - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $container->expects($this->at(0))->method('get')->will($this->returnValue($formFactory)); - - $controller = new TestController(); - $controller->setContainer($container); - - $this->assertEquals($formBuilder, $controller->createFormBuilder('foo')); - } - - public function testGetDoctrine() - { - $doctrine = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry'); - - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $container->expects($this->at(0))->method('has')->will($this->returnValue(true)); - $container->expects($this->at(1))->method('get')->will($this->returnValue($doctrine)); - - $controller = new TestController(); - $controller->setContainer($container); - - $this->assertEquals($doctrine, $controller->getDoctrine()); + return new TestController(); } } class TestController extends Controller { - public function generateUrl($route, $parameters = array(), $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH) - { - return parent::generateUrl($route, $parameters, $referenceType); - } - - public function redirect($url, $status = 302) - { - return parent::redirect($url, $status); - } - - public function forward($controller, array $path = array(), array $query = array()) - { - return parent::forward($controller, $path, $query); - } - - public function getUser() - { - return parent::getUser(); - } - - public function json($data, $status = 200, $headers = array(), $context = array()) - { - return parent::json($data, $status, $headers, $context); - } - - public function file($file, $fileName = null, $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT) - { - return parent::file($file, $fileName, $disposition); - } - - public function isGranted($attributes, $object = null) - { - return parent::isGranted($attributes, $object); - } - - public function denyAccessUnlessGranted($attributes, $object = null, $message = 'Access Denied.') - { - parent::denyAccessUnlessGranted($attributes, $object, $message); - } - - public function redirectToRoute($route, array $parameters = array(), $status = 302) - { - return parent::redirectToRoute($route, $parameters, $status); - } - - public function addFlash($type, $message) - { - parent::addFlash($type, $message); - } - - public function isCsrfTokenValid($id, $token) - { - return parent::isCsrfTokenValid($id, $token); - } - - public function renderView($view, array $parameters = array()) - { - return parent::renderView($view, $parameters); - } - - public function render($view, array $parameters = array(), Response $response = null) - { - return parent::render($view, $parameters, $response); - } - - public function stream($view, array $parameters = array(), StreamedResponse $response = null) - { - return parent::stream($view, $parameters, $response); - } - - public function createNotFoundException($message = 'Not Found', \Exception $previous = null) - { - return parent::createNotFoundException($message, $previous); - } - - public function createAccessDeniedException($message = 'Access Denied.', \Exception $previous = null) - { - return parent::createAccessDeniedException($message, $previous); - } - - public function createForm($type, $data = null, array $options = array()) - { - return parent::createForm($type, $data, $options); - } - - public function createFormBuilder($data = null, array $options = array()) - { - return parent::createFormBuilder($data, $options); - } - - public function getDoctrine() - { - return parent::getDoctrine(); - } + use TestControllerTrait; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTraitTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTraitTest.php new file mode 100644 index 0000000000000..2bd18aa441451 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTraitTest.php @@ -0,0 +1,627 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Controller; + +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\HttpFoundation\BinaryFileResponse; +use Symfony\Component\HttpFoundation\File\File; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\ResponseHeaderBag; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; +use Symfony\Component\HttpFoundation\StreamedResponse; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; +use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; +use Symfony\Component\Security\Core\User\User; +use Symfony\Component\Serializer\SerializerInterface; + +abstract class ControllerTraitTest extends TestCase +{ + abstract protected function createController(); + + public function testForward() + { + $request = Request::create('/'); + $request->setLocale('fr'); + $request->setRequestFormat('xml'); + + $requestStack = new RequestStack(); + $requestStack->push($request); + + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $kernel->expects($this->once())->method('handle')->will($this->returnCallback(function (Request $request) { + return new Response($request->getRequestFormat().'--'.$request->getLocale()); + })); + + $container = new Container(); + $container->set('request_stack', $requestStack); + $container->set('http_kernel', $kernel); + + $controller = $this->createController(); + $controller->setContainer($container); + + $response = $controller->forward('a_controller'); + $this->assertEquals('xml--fr', $response->getContent()); + } + + public function testGetUser() + { + $user = new User('user', 'pass'); + $token = new UsernamePasswordToken($user, 'pass', 'default', array('ROLE_USER')); + + $controller = $this->createController(); + $controller->setContainer($this->getContainerWithTokenStorage($token)); + + $this->assertSame($controller->getUser(), $user); + } + + public function testGetUserAnonymousUserConvertedToNull() + { + $token = new AnonymousToken('default', 'anon.'); + + $controller = $this->createController(); + $controller->setContainer($this->getContainerWithTokenStorage($token)); + + $this->assertNull($controller->getUser()); + } + + public function testGetUserWithEmptyTokenStorage() + { + $controller = $this->createController(); + $controller->setContainer($this->getContainerWithTokenStorage(null)); + + $this->assertNull($controller->getUser()); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage The SecurityBundle is not registered in your application. + */ + public function testGetUserWithEmptyContainer() + { + $controller = $this->createController(); + $controller->setContainer(new Container()); + + $controller->getUser(); + } + + /** + * @param $token + * + * @return Container + */ + private function getContainerWithTokenStorage($token = null) + { + $tokenStorage = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage')->getMock(); + $tokenStorage + ->expects($this->once()) + ->method('getToken') + ->will($this->returnValue($token)); + + $container = new Container(); + $container->set('security.token_storage', $tokenStorage); + + return $container; + } + + public function testJson() + { + $controller = $this->createController(); + $controller->setContainer(new Container()); + + $response = $controller->json(array()); + $this->assertInstanceOf(JsonResponse::class, $response); + $this->assertEquals('[]', $response->getContent()); + } + + public function testJsonWithSerializer() + { + $container = new Container(); + + $serializer = $this->getMockBuilder(SerializerInterface::class)->getMock(); + $serializer + ->expects($this->once()) + ->method('serialize') + ->with(array(), 'json', array('json_encode_options' => JsonResponse::DEFAULT_ENCODING_OPTIONS)) + ->will($this->returnValue('[]')); + + $container->set('serializer', $serializer); + + $controller = $this->createController(); + $controller->setContainer($container); + + $response = $controller->json(array()); + $this->assertInstanceOf(JsonResponse::class, $response); + $this->assertEquals('[]', $response->getContent()); + } + + public function testJsonWithSerializerContextOverride() + { + $container = new Container(); + + $serializer = $this->getMockBuilder(SerializerInterface::class)->getMock(); + $serializer + ->expects($this->once()) + ->method('serialize') + ->with(array(), 'json', array('json_encode_options' => 0, 'other' => 'context')) + ->will($this->returnValue('[]')); + + $container->set('serializer', $serializer); + + $controller = $this->createController(); + $controller->setContainer($container); + + $response = $controller->json(array(), 200, array(), array('json_encode_options' => 0, 'other' => 'context')); + $this->assertInstanceOf(JsonResponse::class, $response); + $this->assertEquals('[]', $response->getContent()); + $response->setEncodingOptions(JSON_FORCE_OBJECT); + $this->assertEquals('{}', $response->getContent()); + } + + public function testFile() + { + $container = new Container(); + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $container->set('http_kernel', $kernel); + + $controller = $this->createController(); + $controller->setContainer($container); + + /* @var BinaryFileResponse $response */ + $response = $controller->file(new File(__FILE__)); + $this->assertInstanceOf(BinaryFileResponse::class, $response); + $this->assertSame(200, $response->getStatusCode()); + if ($response->headers->get('content-type')) { + $this->assertSame('text/x-php', $response->headers->get('content-type')); + } + $this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition')); + $this->assertContains(basename(__FILE__), $response->headers->get('content-disposition')); + } + + public function testFileAsInline() + { + $controller = $this->createController(); + + /* @var BinaryFileResponse $response */ + $response = $controller->file(new File(__FILE__), null, ResponseHeaderBag::DISPOSITION_INLINE); + + $this->assertInstanceOf(BinaryFileResponse::class, $response); + $this->assertSame(200, $response->getStatusCode()); + if ($response->headers->get('content-type')) { + $this->assertSame('text/x-php', $response->headers->get('content-type')); + } + $this->assertContains(ResponseHeaderBag::DISPOSITION_INLINE, $response->headers->get('content-disposition')); + $this->assertContains(basename(__FILE__), $response->headers->get('content-disposition')); + } + + public function testFileWithOwnFileName() + { + $controller = $this->createController(); + + /* @var BinaryFileResponse $response */ + $fileName = 'test.php'; + $response = $controller->file(new File(__FILE__), $fileName); + + $this->assertInstanceOf(BinaryFileResponse::class, $response); + $this->assertSame(200, $response->getStatusCode()); + if ($response->headers->get('content-type')) { + $this->assertSame('text/x-php', $response->headers->get('content-type')); + } + $this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition')); + $this->assertContains($fileName, $response->headers->get('content-disposition')); + } + + public function testFileWithOwnFileNameAsInline() + { + $controller = $this->createController(); + + /* @var BinaryFileResponse $response */ + $fileName = 'test.php'; + $response = $controller->file(new File(__FILE__), $fileName, ResponseHeaderBag::DISPOSITION_INLINE); + + $this->assertInstanceOf(BinaryFileResponse::class, $response); + $this->assertSame(200, $response->getStatusCode()); + if ($response->headers->get('content-type')) { + $this->assertSame('text/x-php', $response->headers->get('content-type')); + } + $this->assertContains(ResponseHeaderBag::DISPOSITION_INLINE, $response->headers->get('content-disposition')); + $this->assertContains($fileName, $response->headers->get('content-disposition')); + } + + public function testFileFromPath() + { + $controller = $this->createController(); + + /* @var BinaryFileResponse $response */ + $response = $controller->file(__FILE__); + + $this->assertInstanceOf(BinaryFileResponse::class, $response); + $this->assertSame(200, $response->getStatusCode()); + if ($response->headers->get('content-type')) { + $this->assertSame('text/x-php', $response->headers->get('content-type')); + } + $this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition')); + $this->assertContains(basename(__FILE__), $response->headers->get('content-disposition')); + } + + public function testFileFromPathWithCustomizedFileName() + { + $controller = $this->createController(); + + /* @var BinaryFileResponse $response */ + $response = $controller->file(__FILE__, 'test.php'); + + $this->assertInstanceOf(BinaryFileResponse::class, $response); + $this->assertSame(200, $response->getStatusCode()); + if ($response->headers->get('content-type')) { + $this->assertSame('text/x-php', $response->headers->get('content-type')); + } + $this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition')); + $this->assertContains('test.php', $response->headers->get('content-disposition')); + } + + /** + * @expectedException \Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException + */ + public function testFileWhichDoesNotExist() + { + $controller = $this->createController(); + + /* @var BinaryFileResponse $response */ + $response = $controller->file('some-file.txt', 'test.php'); + } + + public function testIsGranted() + { + $authorizationChecker = $this->getMockBuilder('Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface')->getMock(); + $authorizationChecker->expects($this->once())->method('isGranted')->willReturn(true); + + $container = new Container(); + $container->set('security.authorization_checker', $authorizationChecker); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertTrue($controller->isGranted('foo')); + } + + /** + * @expectedException \Symfony\Component\Security\Core\Exception\AccessDeniedException + */ + public function testdenyAccessUnlessGranted() + { + $authorizationChecker = $this->getMockBuilder('Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface')->getMock(); + $authorizationChecker->expects($this->once())->method('isGranted')->willReturn(false); + + $container = new Container(); + $container->set('security.authorization_checker', $authorizationChecker); + + $controller = $this->createController(); + $controller->setContainer($container); + + $controller->denyAccessUnlessGranted('foo'); + } + + public function testRenderViewTwig() + { + $twig = $this->getMockBuilder('\Twig_Environment')->disableOriginalConstructor()->getMock(); + $twig->expects($this->once())->method('render')->willReturn('bar'); + + $container = new Container(); + $container->set('twig', $twig); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertEquals('bar', $controller->renderView('foo')); + } + + public function testRenderTwig() + { + $twig = $this->getMockBuilder('\Twig_Environment')->disableOriginalConstructor()->getMock(); + $twig->expects($this->once())->method('render')->willReturn('bar'); + + $container = new Container(); + $container->set('twig', $twig); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertEquals('bar', $controller->render('foo')->getContent()); + } + + public function testStreamTwig() + { + $twig = $this->getMockBuilder('\Twig_Environment')->disableOriginalConstructor()->getMock(); + + $container = new Container(); + $container->set('twig', $twig); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $controller->stream('foo')); + } + + public function testRedirectToRoute() + { + $router = $this->getMockBuilder('Symfony\Component\Routing\RouterInterface')->getMock(); + $router->expects($this->once())->method('generate')->willReturn('/foo'); + + $container = new Container(); + $container->set('router', $router); + + $controller = $this->createController(); + $controller->setContainer($container); + $response = $controller->redirectToRoute('foo'); + + $this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $response); + $this->assertSame('/foo', $response->getTargetUrl()); + $this->assertSame(302, $response->getStatusCode()); + } + + public function testAddFlash() + { + $flashBag = new FlashBag(); + $session = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\Session')->getMock(); + $session->expects($this->once())->method('getFlashBag')->willReturn($flashBag); + + $container = new Container(); + $container->set('session', $session); + + $controller = $this->createController(); + $controller->setContainer($container); + $controller->addFlash('foo', 'bar'); + + $this->assertSame(array('bar'), $flashBag->get('foo')); + } + + public function testCreateAccessDeniedException() + { + $controller = $this->createController(); + + $this->assertInstanceOf('Symfony\Component\Security\Core\Exception\AccessDeniedException', $controller->createAccessDeniedException()); + } + + public function testIsCsrfTokenValid() + { + $tokenManager = $this->getMockBuilder('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface')->getMock(); + $tokenManager->expects($this->once())->method('isTokenValid')->willReturn(true); + + $container = new Container(); + $container->set('security.csrf.token_manager', $tokenManager); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertTrue($controller->isCsrfTokenValid('foo', 'bar')); + } + + public function testGenerateUrl() + { + $router = $this->getMockBuilder('Symfony\Component\Routing\RouterInterface')->getMock(); + $router->expects($this->once())->method('generate')->willReturn('/foo'); + + $container = new Container(); + $container->set('router', $router); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertEquals('/foo', $controller->generateUrl('foo')); + } + + public function testRedirect() + { + $controller = $this->createController(); + $response = $controller->redirect('http://dunglas.fr', 301); + + $this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $response); + $this->assertSame('http://dunglas.fr', $response->getTargetUrl()); + $this->assertSame(301, $response->getStatusCode()); + } + + public function testRenderViewTemplating() + { + $templating = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Templating\EngineInterface')->getMock(); + $templating->expects($this->once())->method('render')->willReturn('bar'); + + $container = new Container(); + $container->set('templating', $templating); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertEquals('bar', $controller->renderView('foo')); + } + + public function testRenderTemplating() + { + $templating = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Templating\EngineInterface')->getMock(); + $templating->expects($this->once())->method('renderResponse')->willReturn(new Response('bar')); + + $container = new Container(); + $container->set('templating', $templating); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertEquals('bar', $controller->render('foo')->getContent()); + } + + public function testStreamTemplating() + { + $templating = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Templating\EngineInterface')->getMock(); + + $container = new Container(); + $container->set('templating', $templating); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $controller->stream('foo')); + } + + public function testCreateNotFoundException() + { + $controller = $this->createController(); + + $this->assertInstanceOf('Symfony\Component\HttpKernel\Exception\NotFoundHttpException', $controller->createNotFoundException()); + } + + public function testCreateForm() + { + $form = $this->getMockBuilder('Symfony\Component\Form\FormInterface')->getMock(); + + $formFactory = $this->getMockBuilder('Symfony\Component\Form\FormFactoryInterface')->getMock(); + $formFactory->expects($this->once())->method('create')->willReturn($form); + + $container = new Container(); + $container->set('form.factory', $formFactory); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertEquals($form, $controller->createForm('foo')); + } + + public function testCreateFormBuilder() + { + $formBuilder = $this->getMockBuilder('Symfony\Component\Form\FormBuilderInterface')->getMock(); + + $formFactory = $this->getMockBuilder('Symfony\Component\Form\FormFactoryInterface')->getMock(); + $formFactory->expects($this->once())->method('createBuilder')->willReturn($formBuilder); + + $container = new Container(); + $container->set('form.factory', $formFactory); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertEquals($formBuilder, $controller->createFormBuilder('foo')); + } + + public function testGetDoctrine() + { + $doctrine = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')->getMock(); + + $container = new Container(); + $container->set('doctrine', $doctrine); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertEquals($doctrine, $controller->getDoctrine()); + } +} + +trait TestControllerTrait +{ + public function generateUrl($route, $parameters = array(), $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH) + { + return parent::generateUrl($route, $parameters, $referenceType); + } + + public function redirect($url, $status = 302) + { + return parent::redirect($url, $status); + } + + public function forward($controller, array $path = array(), array $query = array()) + { + return parent::forward($controller, $path, $query); + } + + public function getUser() + { + return parent::getUser(); + } + + public function json($data, $status = 200, $headers = array(), $context = array()) + { + return parent::json($data, $status, $headers, $context); + } + + public function file($file, $fileName = null, $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT) + { + return parent::file($file, $fileName, $disposition); + } + + public function isGranted($attributes, $object = null) + { + return parent::isGranted($attributes, $object); + } + + public function denyAccessUnlessGranted($attributes, $object = null, $message = 'Access Denied.') + { + parent::denyAccessUnlessGranted($attributes, $object, $message); + } + + public function redirectToRoute($route, array $parameters = array(), $status = 302) + { + return parent::redirectToRoute($route, $parameters, $status); + } + + public function addFlash($type, $message) + { + parent::addFlash($type, $message); + } + + public function isCsrfTokenValid($id, $token) + { + return parent::isCsrfTokenValid($id, $token); + } + + public function renderView($view, array $parameters = array()) + { + return parent::renderView($view, $parameters); + } + + public function render($view, array $parameters = array(), Response $response = null) + { + return parent::render($view, $parameters, $response); + } + + public function stream($view, array $parameters = array(), StreamedResponse $response = null) + { + return parent::stream($view, $parameters, $response); + } + + public function createNotFoundException($message = 'Not Found', \Exception $previous = null) + { + return parent::createNotFoundException($message, $previous); + } + + public function createAccessDeniedException($message = 'Access Denied.', \Exception $previous = null) + { + return parent::createAccessDeniedException($message, $previous); + } + + public function createForm($type, $data = null, array $options = array()) + { + return parent::createForm($type, $data, $options); + } + + public function createFormBuilder($data = null, array $options = array()) + { + return parent::createFormBuilder($data, $options); + } + + public function getDoctrine() + { + return parent::getDoctrine(); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php index eaca189330599..14b6e4428e550 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php @@ -66,14 +66,14 @@ public function testRoute($permanent, $ignoreAttributes, $expectedCode, $expecte $request->attributes = new ParameterBag($attributes); - $router = $this->getMock('Symfony\Component\Routing\RouterInterface'); + $router = $this->getMockBuilder('Symfony\Component\Routing\RouterInterface')->getMock(); $router ->expects($this->once()) ->method('generate') ->with($this->equalTo($route), $this->equalTo($expectedAttributes)) ->will($this->returnValue($url)); - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); $container ->expects($this->once()) @@ -230,7 +230,7 @@ public function testPathQueryParams($expectedUrl, $path, $queryString) private function createRequestObject($scheme, $host, $port, $baseUrl, $queryString = '') { - $request = $this->getMock('Symfony\Component\HttpFoundation\Request'); + $request = $this->getMockBuilder('Symfony\Component\HttpFoundation\Request')->getMock(); $request ->expects($this->any()) ->method('getScheme') @@ -257,7 +257,7 @@ private function createRequestObject($scheme, $host, $port, $baseUrl, $queryStri private function createRedirectController($httpPort = null, $httpsPort = null) { - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); if (null !== $httpPort) { $container diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/TemplateControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/TemplateControllerTest.php new file mode 100644 index 0000000000000..04e6447ee93ea --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/TemplateControllerTest.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Controller; + +use Symfony\Bundle\FrameworkBundle\Controller\TemplateController; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Component\HttpFoundation\Response; + +/** + * @author Kévin Dunglas + */ +class TemplateControllerTest extends TestCase +{ + public function testTwig() + { + $twig = $this->getMockBuilder('\Twig_Environment')->disableOriginalConstructor()->getMock(); + $twig->expects($this->once())->method('render')->willReturn('bar'); + + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); + $container->expects($this->at(0))->method('has')->will($this->returnValue(false)); + $container->expects($this->at(1))->method('has')->will($this->returnValue(true)); + $container->expects($this->at(2))->method('get')->will($this->returnValue($twig)); + + $controller = new TemplateController(); + $controller->setContainer($container); + + $this->assertEquals('bar', $controller->templateAction('mytemplate')->getContent()); + } + + public function testTemplating() + { + $templating = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Templating\EngineInterface')->getMock(); + $templating->expects($this->once())->method('renderResponse')->willReturn(new Response('bar')); + + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); + $container->expects($this->at(0))->method('has')->willReturn(true); + $container->expects($this->at(1))->method('get')->will($this->returnValue($templating)); + + $controller = new TemplateController(); + $controller->setContainer($container); + + $this->assertEquals('bar', $controller->templateAction('mytemplate')->getContent()); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage You can not use the TemplateController if the Templating Component or the Twig Bundle are not available. + */ + public function testNoTwigNorTemplating() + { + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); + $container->expects($this->at(0))->method('has')->willReturn(false); + $container->expects($this->at(1))->method('has')->willReturn(false); + + $controller = new TemplateController(); + $controller->setContainer($container); + + $controller->templateAction('mytemplate')->getContent(); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddCacheWarmerPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddCacheWarmerPassTest.php index 204fdf09b653e..3516aa93608fc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddCacheWarmerPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddCacheWarmerPassTest.php @@ -11,56 +11,37 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddCacheWarmerPass; -class AddCacheWarmerPassTest extends \PHPUnit_Framework_TestCase +class AddCacheWarmerPassTest extends TestCase { public function testThatCacheWarmersAreProcessedInPriorityOrder() { - $services = array( - 'my_cache_warmer_service1' => array(0 => array('priority' => 100)), - 'my_cache_warmer_service2' => array(0 => array('priority' => 200)), - 'my_cache_warmer_service3' => array(0 => array()), - ); - - $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); - $container = $this->getMock( - 'Symfony\Component\DependencyInjection\ContainerBuilder', - array('findTaggedServiceIds', 'getDefinition', 'hasDefinition') - ); - - $container->expects($this->atLeastOnce()) - ->method('findTaggedServiceIds') - ->will($this->returnValue($services)); - $container->expects($this->atLeastOnce()) - ->method('getDefinition') - ->with('cache_warmer') - ->will($this->returnValue($definition)); - $container->expects($this->atLeastOnce()) - ->method('hasDefinition') - ->with('cache_warmer') - ->will($this->returnValue(true)); + $container = new ContainerBuilder(); - $definition->expects($this->once()) - ->method('replaceArgument') - ->with(0, array( - new Reference('my_cache_warmer_service2'), - new Reference('my_cache_warmer_service1'), - new Reference('my_cache_warmer_service3'), - )); + $definition = $container->register('cache_warmer')->addArgument(null); + $container->register('my_cache_warmer_service1')->addTag('kernel.cache_warmer', array('priority' => 100)); + $container->register('my_cache_warmer_service2')->addTag('kernel.cache_warmer', array('priority' => 200)); + $container->register('my_cache_warmer_service3')->addTag('kernel.cache_warmer'); $addCacheWarmerPass = new AddCacheWarmerPass(); $addCacheWarmerPass->process($container); + + $expected = array( + new Reference('my_cache_warmer_service2'), + new Reference('my_cache_warmer_service1'), + new Reference('my_cache_warmer_service3'), + ); + $this->assertEquals($expected, $definition->getArgument(0)); } public function testThatCompilerPassIsIgnoredIfThereIsNoCacheWarmerDefinition() { - $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); - $container = $this->getMock( - 'Symfony\Component\DependencyInjection\ContainerBuilder', - array('hasDefinition', 'findTaggedServiceIds', 'getDefinition') - ); + $definition = $this->getMockBuilder('Symfony\Component\DependencyInjection\Definition')->getMock(); + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->setMethods(array('hasDefinition', 'findTaggedServiceIds', 'getDefinition'))->getMock(); $container->expects($this->never())->method('findTaggedServiceIds'); $container->expects($this->never())->method('getDefinition'); @@ -76,11 +57,8 @@ public function testThatCompilerPassIsIgnoredIfThereIsNoCacheWarmerDefinition() public function testThatCacheWarmersMightBeNotDefined() { - $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); - $container = $this->getMock( - 'Symfony\Component\DependencyInjection\ContainerBuilder', - array('hasDefinition', 'findTaggedServiceIds', 'getDefinition') - ); + $definition = $this->getMockBuilder('Symfony\Component\DependencyInjection\Definition')->getMock(); + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->setMethods(array('hasDefinition', 'findTaggedServiceIds', 'getDefinition'))->getMock(); $container->expects($this->atLeastOnce()) ->method('findTaggedServiceIds') diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConsoleCommandPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConsoleCommandPassTest.php index ab0bc985703ad..58a0da41c3d91 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConsoleCommandPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConsoleCommandPassTest.php @@ -11,13 +11,17 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; +use PHPUnit\Framework\TestCase; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConsoleCommandPass; use Symfony\Component\Console\Command\Command; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\HttpKernel\Bundle\Bundle; -class AddConsoleCommandPassTest extends \PHPUnit_Framework_TestCase +/** + * @group legacy + */ +class AddConsoleCommandPassTest extends TestCase { /** * @dataProvider visibilityProvider @@ -36,18 +40,20 @@ public function testProcess($public) $container->compile(); $alias = 'console.command.symfony_bundle_frameworkbundle_tests_dependencyinjection_compiler_mycommand'; - if ($container->hasAlias($alias)) { - $this->assertSame('my-command', (string) $container->getAlias($alias)); + + if ($public) { + $this->assertFalse($container->hasAlias($alias)); + $id = 'my-command'; } else { + $id = $alias; // The alias is replaced by a Definition by the ReplaceAliasByActualDefinitionPass // in case the original service is private $this->assertFalse($container->hasDefinition('my-command')); $this->assertTrue($container->hasDefinition($alias)); } - $id = $public ? 'my-command' : 'console.command.symfony_bundle_frameworkbundle_tests_dependencyinjection_compiler_mycommand'; $this->assertTrue($container->hasParameter('console.command.ids')); - $this->assertSame(array($id), $container->getParameter('console.command.ids')); + $this->assertSame(array($alias => $id), $container->getParameter('console.command.ids')); } public function visibilityProvider() @@ -90,6 +96,28 @@ public function testProcessThrowAnExceptionIfTheServiceIsNotASubclassOfCommand() $container->compile(); } + + public function testProcessPrivateServicesWithSameCommand() + { + $container = new ContainerBuilder(); + $className = 'Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\MyCommand'; + + $definition1 = new Definition($className); + $definition1->addTag('console.command')->setPublic(false); + + $definition2 = new Definition($className); + $definition2->addTag('console.command')->setPublic(false); + + $container->setDefinition('my-command1', $definition1); + $container->setDefinition('my-command2', $definition2); + + (new AddConsoleCommandPass())->process($container); + + $alias1 = 'console.command.symfony_bundle_frameworkbundle_tests_dependencyinjection_compiler_mycommand'; + $alias2 = $alias1.'_my-command2'; + $this->assertTrue($container->hasAlias($alias1)); + $this->assertTrue($container->hasAlias($alias2)); + } } class MyCommand extends Command diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConstraintValidatorsPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConstraintValidatorsPassTest.php index 0629d1ebafd1f..d9065e46d5693 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConstraintValidatorsPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConstraintValidatorsPassTest.php @@ -9,57 +9,56 @@ * file that was distributed with this source code. */ +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConstraintValidatorsPass; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ServiceLocator; -class AddConstraintValidatorsPassTest extends \PHPUnit_Framework_TestCase +/** + * @group legacy + */ +class AddConstraintValidatorsPassTest extends TestCase { public function testThatConstraintValidatorServicesAreProcessed() { - $services = array( - 'my_constraint_validator_service1' => array(0 => array('alias' => 'my_constraint_validator_alias1')), - 'my_constraint_validator_service2' => array(), - ); - - $validatorFactoryDefinition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); - $container = $this->getMock( - 'Symfony\Component\DependencyInjection\ContainerBuilder', - array('findTaggedServiceIds', 'getDefinition', 'hasDefinition') - ); + $container = new ContainerBuilder(); + $validatorFactory = $container->register('validator.validator_factory') + ->addArgument(array()); - $validatorDefinition1 = $this->getMock('Symfony\Component\DependencyInjection\Definition', array('getClass')); - $validatorDefinition2 = $this->getMock('Symfony\Component\DependencyInjection\Definition', array('getClass')); + $container->register('my_constraint_validator_service1', Validator1::class) + ->addTag('validator.constraint_validator', array('alias' => 'my_constraint_validator_alias1')); + $container->register('my_constraint_validator_service2', Validator2::class) + ->addTag('validator.constraint_validator'); - $validatorDefinition1->expects($this->atLeastOnce()) - ->method('getClass') - ->willReturn('My\Fully\Qualified\Class\Named\Validator1'); - $validatorDefinition2->expects($this->atLeastOnce()) - ->method('getClass') - ->willReturn('My\Fully\Qualified\Class\Named\Validator2'); + $addConstraintValidatorsPass = new AddConstraintValidatorsPass(); + $addConstraintValidatorsPass->process($container); - $container->expects($this->any()) - ->method('getDefinition') - ->with($this->anything()) - ->will($this->returnValueMap(array( - array('my_constraint_validator_service1', $validatorDefinition1), - array('my_constraint_validator_service2', $validatorDefinition2), - array('validator.validator_factory', $validatorFactoryDefinition), - ))); + $expected = (new Definition(ServiceLocator::class, array(array( + Validator1::class => new ServiceClosureArgument(new Reference('my_constraint_validator_service1')), + 'my_constraint_validator_alias1' => new ServiceClosureArgument(new Reference('my_constraint_validator_service1')), + Validator2::class => new ServiceClosureArgument(new Reference('my_constraint_validator_service2')), + ))))->addTag('container.service_locator')->setPublic(false); + $this->assertEquals($expected, $container->getDefinition((string) $validatorFactory->getArgument(0))); + } - $container->expects($this->atLeastOnce()) - ->method('findTaggedServiceIds') - ->will($this->returnValue($services)); - $container->expects($this->atLeastOnce()) - ->method('hasDefinition') - ->with('validator.validator_factory') - ->will($this->returnValue(true)); + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The service "my_abstract_constraint_validator" tagged "validator.constraint_validator" must not be abstract. + */ + public function testAbstractConstraintValidator() + { + $container = new ContainerBuilder(); + $validatorFactory = $container->register('validator.validator_factory') + ->addArgument(array()); - $validatorFactoryDefinition->expects($this->once()) - ->method('replaceArgument') - ->with(1, array( - 'My\Fully\Qualified\Class\Named\Validator1' => 'my_constraint_validator_service1', - 'my_constraint_validator_alias1' => 'my_constraint_validator_service1', - 'My\Fully\Qualified\Class\Named\Validator2' => 'my_constraint_validator_service2', - )); + $container->register('my_abstract_constraint_validator') + ->setAbstract(true) + ->addTag('validator.constraint_validator'); $addConstraintValidatorsPass = new AddConstraintValidatorsPass(); $addConstraintValidatorsPass->process($container); @@ -67,11 +66,8 @@ public function testThatConstraintValidatorServicesAreProcessed() public function testThatCompilerPassIsIgnoredIfThereIsNoConstraintValidatorFactoryDefinition() { - $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); - $container = $this->getMock( - 'Symfony\Component\DependencyInjection\ContainerBuilder', - array('hasDefinition', 'findTaggedServiceIds', 'getDefinition') - ); + $definition = $this->getMockBuilder('Symfony\Component\DependencyInjection\Definition')->getMock(); + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->setMethods(array('hasDefinition', 'findTaggedServiceIds', 'getDefinition'))->getMock(); $container->expects($this->never())->method('findTaggedServiceIds'); $container->expects($this->never())->method('getDefinition'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddExpressionLanguageProvidersPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddExpressionLanguageProvidersPassTest.php index 9bc4acb9c1567..0934fe31c0c40 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddExpressionLanguageProvidersPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddExpressionLanguageProvidersPassTest.php @@ -11,12 +11,13 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; +use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddExpressionLanguageProvidersPass; -class AddExpressionLanguageProvidersPassTest extends \PHPUnit_Framework_TestCase +class AddExpressionLanguageProvidersPassTest extends TestCase { public function testProcessForRouter() { diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolClearerPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolClearerPassTest.php index 38a2d38761e3b..9230405d7560e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolClearerPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolClearerPassTest.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; +use PHPUnit\Framework\TestCase; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolClearerPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolPass; use Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass; @@ -18,8 +19,9 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\HttpKernel\CacheClearer\Psr6CacheClearer; -class CachePoolClearerPassTest extends \PHPUnit_Framework_TestCase +class CachePoolClearerPassTest extends TestCase { public function testPoolRefsAreWeak() { @@ -29,6 +31,9 @@ public function testPoolRefsAreWeak() $container->setParameter('kernel.environment', 'prod'); $container->setParameter('kernel.root_dir', 'foo'); + $globalClearer = new Definition(Psr6CacheClearer::class); + $container->setDefinition('cache.global_clearer', $globalClearer); + $publicPool = new Definition(); $publicPool->addArgument('namespace'); $publicPool->addTag('cache.pool', array('clearer' => 'clearer_alias')); @@ -50,6 +55,7 @@ public function testPoolRefsAreWeak() $pass->process($container); } - $this->assertEquals(array(array('addPool', array(new Reference('public.pool')))), $clearer->getMethodCalls()); + $this->assertEquals(array(array('public.pool' => new Reference('public.pool'))), $clearer->getArguments()); + $this->assertEquals(array(array('public.pool' => new Reference('public.pool'))), $globalClearer->getArguments()); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php index 192e1493d0c38..713cd4eaca842 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php @@ -11,13 +11,14 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; +use PHPUnit\Framework\TestCase; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolPass; +use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\Reference; -class CachePoolPassTest extends \PHPUnit_Framework_TestCase +class CachePoolPassTest extends TestCase { private $cachePoolPass; @@ -38,14 +39,14 @@ public function testNamespaceArgumentIsReplaced() $adapter->addTag('cache.pool'); $container->setDefinition('app.cache_adapter', $adapter); $container->setAlias('app.cache_adapter_alias', 'app.cache_adapter'); - $cachePool = new DefinitionDecorator('app.cache_adapter_alias'); + $cachePool = new ChildDefinition('app.cache_adapter_alias'); $cachePool->addArgument(null); $cachePool->addTag('cache.pool'); $container->setDefinition('app.cache_pool', $cachePool); $this->cachePoolPass->process($container); - $this->assertSame('C42Pcl9VBJ', $cachePool->getArgument(0)); + $this->assertSame('D07rhFx97S', $cachePool->getArgument(0)); } public function testArgsAreReplaced() @@ -69,7 +70,7 @@ public function testArgsAreReplaced() $this->assertInstanceOf(Reference::class, $cachePool->getArgument(0)); $this->assertSame('foobar', (string) $cachePool->getArgument(0)); - $this->assertSame('KO3xHaFEZU', $cachePool->getArgument(1)); + $this->assertSame('itantF+pIq', $cachePool->getArgument(1)); $this->assertSame(3, $cachePool->getArgument(2)); } @@ -88,7 +89,7 @@ public function testThrowsExceptionWhenCachePoolTagHasUnknownAttributes() $adapter->setAbstract(true); $adapter->addTag('cache.pool'); $container->setDefinition('app.cache_adapter', $adapter); - $cachePool = new DefinitionDecorator('app.cache_adapter'); + $cachePool = new ChildDefinition('app.cache_adapter'); $cachePool->addTag('cache.pool', array('foobar' => 123)); $container->setDefinition('app.cache_pool', $cachePool); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ConfigCachePassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ConfigCachePassTest.php index 48c753b6e2d45..e2348972d09c7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ConfigCachePassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ConfigCachePassTest.php @@ -11,51 +11,40 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ConfigCachePass; -class ConfigCachePassTest extends \PHPUnit_Framework_TestCase +/** + * @group legacy + */ +class ConfigCachePassTest extends TestCase { public function testThatCheckersAreProcessedInPriorityOrder() { - $services = array( - 'checker_2' => array(0 => array('priority' => 100)), - 'checker_1' => array(0 => array('priority' => 200)), - 'checker_3' => array(0 => array()), - ); + $container = new ContainerBuilder(); - $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); - $container = $this->getMock( - 'Symfony\Component\DependencyInjection\ContainerBuilder', - array('findTaggedServiceIds', 'getDefinition', 'hasDefinition') - ); - - $container->expects($this->atLeastOnce()) - ->method('findTaggedServiceIds') - ->will($this->returnValue($services)); - $container->expects($this->atLeastOnce()) - ->method('getDefinition') - ->with('config_cache_factory') - ->will($this->returnValue($definition)); - - $definition->expects($this->once()) - ->method('replaceArgument') - ->with(0, array( - new Reference('checker_1'), - new Reference('checker_2'), - new Reference('checker_3'), - )); + $definition = $container->register('config_cache_factory')->addArgument(null); + $container->register('checker_2')->addTag('config_cache.resource_checker', array('priority' => 100)); + $container->register('checker_1')->addTag('config_cache.resource_checker', array('priority' => 200)); + $container->register('checker_3')->addTag('config_cache.resource_checker'); $pass = new ConfigCachePass(); $pass->process($container); + + $expected = new IteratorArgument(array( + new Reference('checker_1'), + new Reference('checker_2'), + new Reference('checker_3'), + )); + $this->assertEquals($expected, $definition->getArgument(0)); } public function testThatCheckersCanBeMissing() { - $container = $this->getMock( - 'Symfony\Component\DependencyInjection\ContainerBuilder', - array('findTaggedServiceIds') - ); + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->setMethods(array('findTaggedServiceIds'))->getMock(); $container->expects($this->atLeastOnce()) ->method('findTaggedServiceIds') diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ControllerArgumentValueResolverPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ControllerArgumentValueResolverPassTest.php index 5afcb4bb51994..1adfdf2734f0c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ControllerArgumentValueResolverPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ControllerArgumentValueResolverPassTest.php @@ -11,13 +11,17 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; +use PHPUnit\Framework\TestCase; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ControllerArgumentValueResolverPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpKernel\Controller\ArgumentResolver; -class ControllerArgumentValueResolverPassTest extends \PHPUnit_Framework_TestCase +/** + * @group legacy + */ +class ControllerArgumentValueResolverPassTest extends TestCase { public function testServicesAreOrderedAccordingToPriority() { @@ -42,7 +46,7 @@ public function testServicesAreOrderedAccordingToPriority() } (new ControllerArgumentValueResolverPass())->process($container); - $this->assertEquals($expected, $definition->getArgument(1)); + $this->assertEquals($expected, $definition->getArgument(1)->getValues()); } public function testReturningEmptyArrayWhenNoService() @@ -52,7 +56,7 @@ public function testReturningEmptyArrayWhenNoService() $container->setDefinition('argument_resolver', $definition); (new ControllerArgumentValueResolverPass())->process($container); - $this->assertEquals(array(), $definition->getArgument(1)); + $this->assertEquals(array(), $definition->getArgument(1)->getValues()); } public function testNoArgumentResolver() diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/DataCollectorTranslatorPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/DataCollectorTranslatorPassTest.php new file mode 100644 index 0000000000000..2d6e8ca3be478 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/DataCollectorTranslatorPassTest.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\DataCollectorTranslatorPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\Translation\TranslatorInterface; + +class DataCollectorTranslatorPassTest extends TestCase +{ + private $container; + private $dataCollectorTranslatorPass; + + protected function setUp() + { + $this->container = new ContainerBuilder(); + $this->dataCollectorTranslatorPass = new DataCollectorTranslatorPass(); + + $this->container->setParameter('translator_implementing_bag', 'Symfony\Component\Translation\Translator'); + $this->container->setParameter('translator_not_implementing_bag', 'Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\TranslatorWithTranslatorBag'); + + $this->container->register('translator.data_collector', 'Symfony\Component\Translation\DataCollectorTranslator') + ->setPublic(false) + ->setDecoratedService('translator') + ->setArguments(array(new Reference('translator.data_collector.inner'))) + ; + + $this->container->register('data_collector.translation', 'Symfony\Component\Translation\DataCollector\TranslationDataCollector') + ->setArguments(array(new Reference('translator.data_collector'))) + ; + } + + /** + * @dataProvider getImplementingTranslatorBagInterfaceTranslatorClassNames + */ + public function testProcessKeepsDataCollectorTranslatorIfItImplementsTranslatorBagInterface($class) + { + $this->container->register('translator', $class); + + $this->dataCollectorTranslatorPass->process($this->container); + + $this->assertTrue($this->container->hasDefinition('translator.data_collector')); + } + + /** + * @dataProvider getImplementingTranslatorBagInterfaceTranslatorClassNames + */ + public function testProcessKeepsDataCollectorIfTranslatorImplementsTranslatorBagInterface($class) + { + $this->container->register('translator', $class); + + $this->dataCollectorTranslatorPass->process($this->container); + + $this->assertTrue($this->container->hasDefinition('data_collector.translation')); + } + + public function getImplementingTranslatorBagInterfaceTranslatorClassNames() + { + return array( + array('Symfony\Component\Translation\Translator'), + array('%translator_implementing_bag%'), + ); + } + + /** + * @dataProvider getNotImplementingTranslatorBagInterfaceTranslatorClassNames + */ + public function testProcessRemovesDataCollectorTranslatorIfItDoesNotImplementTranslatorBagInterface($class) + { + $this->container->register('translator', $class); + + $this->dataCollectorTranslatorPass->process($this->container); + + $this->assertFalse($this->container->hasDefinition('translator.data_collector')); + } + + /** + * @dataProvider getNotImplementingTranslatorBagInterfaceTranslatorClassNames + */ + public function testProcessRemovesDataCollectorIfTranslatorDoesNotImplementTranslatorBagInterface($class) + { + $this->container->register('translator', $class); + + $this->dataCollectorTranslatorPass->process($this->container); + + $this->assertFalse($this->container->hasDefinition('data_collector.translation')); + } + + public function getNotImplementingTranslatorBagInterfaceTranslatorClassNames() + { + return array( + array('Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\TranslatorWithTranslatorBag'), + array('%translator_not_implementing_bag%'), + ); + } +} + +class TranslatorWithTranslatorBag implements TranslatorInterface +{ + public function trans($id, array $parameters = array(), $domain = null, $locale = null) + { + } + + public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) + { + } + + public function setLocale($locale) + { + } + + public function getLocale() + { + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/FormPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/FormPassTest.php index ec80b9551063f..9adbaf8da9062 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/FormPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/FormPassTest.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; +use PHPUnit\Framework\TestCase; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FormPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; @@ -18,9 +19,11 @@ use Symfony\Component\Form\AbstractType; /** + * @group legacy + * * @author Bernhard Schussek */ -class FormPassTest extends \PHPUnit_Framework_TestCase +class FormPassTest extends TestCase { public function testDoNothingIfFormExtensionNotLoaded() { @@ -178,7 +181,7 @@ public function testAddTaggedGuessers() /** * @dataProvider privateTaggedServicesProvider */ - public function testPrivateTaggedServices($id, $tagName, $expectedExceptionMessage) + public function testPrivateTaggedServices($id, $tagName) { $container = new ContainerBuilder(); $container->addCompilerPass(new FormPass()); @@ -192,19 +195,18 @@ public function testPrivateTaggedServices($id, $tagName, $expectedExceptionMessa )); $container->setDefinition('form.extension', $extDefinition); - $container->register($id, 'stdClass')->setPublic(false)->addTag($tagName); - - $this->setExpectedException('\InvalidArgumentException', $expectedExceptionMessage); + $container->register($id, 'stdClass')->setPublic(false)->addTag($tagName, array('extended_type' => 'Foo')); $container->compile(); + $this->assertTrue($container->getDefinition($id)->isPublic()); } public function privateTaggedServicesProvider() { return array( - array('my.type', 'form.type', 'The service "my.type" must be public as form types are lazy-loaded'), - array('my.type_extension', 'form.type_extension', 'The service "my.type_extension" must be public as form type extensions are lazy-loaded'), - array('my.guesser', 'form.type_guesser', 'The service "my.guesser" must be public as form type guessers are lazy-loaded'), + array('my.type', 'form.type'), + array('my.type_extension', 'form.type_extension'), + array('my.guesser', 'form.type_guesser'), ); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/LoggingTranslatorPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/LoggingTranslatorPassTest.php index 77f894faa9d68..eda507c621ebf 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/LoggingTranslatorPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/LoggingTranslatorPassTest.php @@ -11,15 +11,16 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; +use PHPUnit\Framework\TestCase; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\LoggingTranslatorPass; -class LoggingTranslatorPassTest extends \PHPUnit_Framework_TestCase +class LoggingTranslatorPassTest extends TestCase { public function testProcess() { - $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder'); - $parameterBag = $this->getMock('Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface'); + $definition = $this->getMockBuilder('Symfony\Component\DependencyInjection\Definition')->getMock(); + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->getMock(); + $parameterBag = $this->getMockBuilder('Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface')->getMock(); $container->expects($this->exactly(2)) ->method('hasAlias') @@ -54,13 +55,18 @@ public function testProcess() ->method('getParameterBag') ->will($this->returnValue($parameterBag)); + $container->expects($this->once()) + ->method('getReflectionClass') + ->with('Symfony\Bundle\FrameworkBundle\Translation\Translator') + ->will($this->returnValue(new \ReflectionClass('Symfony\Bundle\FrameworkBundle\Translation\Translator'))); + $pass = new LoggingTranslatorPass(); $pass->process($container); } public function testThatCompilerPassIsIgnoredIfThereIsNotLoggerDefinition() { - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder'); + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->getMock(); $container->expects($this->once()) ->method('hasAlias') ->will($this->returnValue(false)); @@ -71,7 +77,7 @@ public function testThatCompilerPassIsIgnoredIfThereIsNotLoggerDefinition() public function testThatCompilerPassIsIgnoredIfThereIsNotTranslatorDefinition() { - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder'); + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->getMock(); $container->expects($this->at(0)) ->method('hasAlias') ->will($this->returnValue(true)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ProfilerPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ProfilerPassTest.php index f2af872e3c1f9..e064ce9f17f02 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ProfilerPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ProfilerPassTest.php @@ -11,10 +11,11 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; +use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Definition; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ProfilerPass; -class ProfilerPassTest extends \PHPUnit_Framework_TestCase +class ProfilerPassTest extends TestCase { private $profilerDefinition; @@ -40,7 +41,7 @@ public function testTemplateNoIdThrowsException() $builder = $this->createContainerMock($services); - $this->setExpectedException('InvalidArgumentException'); + $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('InvalidArgumentException'); $profilerPass = new ProfilerPass(); $profilerPass->process($builder); @@ -75,10 +76,7 @@ public function testValidCollector() private function createContainerMock($services) { - $container = $this->getMock( - 'Symfony\Component\DependencyInjection\ContainerBuilder', - array('hasDefinition', 'getDefinition', 'findTaggedServiceIds', 'setParameter') - ); + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->setMethods(array('hasDefinition', 'getDefinition', 'findTaggedServiceIds', 'setParameter'))->getMock(); $container->expects($this->any()) ->method('hasDefinition') ->with($this->equalTo('profiler')) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/PropertyInfoPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/PropertyInfoPassTest.php index fca0f3461ff8a..19b25bccb9729 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/PropertyInfoPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/PropertyInfoPassTest.php @@ -11,48 +11,53 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; +use PHPUnit\Framework\TestCase; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\PropertyInfoPass; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; -class PropertyInfoPassTest extends \PHPUnit_Framework_TestCase +/** + * @group legacy + */ +class PropertyInfoPassTest extends TestCase { - public function testServicesAreOrderedAccordingToPriority() + /** + * @dataProvider provideTags + */ + public function testServicesAreOrderedAccordingToPriority($index, $tag) { - $services = array( - 'n3' => array('tag' => array()), - 'n1' => array('tag' => array('priority' => 200)), - 'n2' => array('tag' => array('priority' => 100)), - ); + $container = new ContainerBuilder(); + + $definition = $container->register('property_info')->setArguments(array(null, null, null, null)); + $container->register('n2')->addTag($tag, array('priority' => 100)); + $container->register('n1')->addTag($tag, array('priority' => 200)); + $container->register('n3')->addTag($tag); - $expected = array( + $propertyInfoPass = new PropertyInfoPass(); + $propertyInfoPass->process($container); + + $expected = new IteratorArgument(array( new Reference('n1'), new Reference('n2'), new Reference('n3'), - ); - - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder', array('findTaggedServiceIds')); - - $container - ->expects($this->any()) - ->method('findTaggedServiceIds') - ->will($this->returnValue($services)); - - $propertyInfoPass = new PropertyInfoPass(); + )); + $this->assertEquals($expected, $definition->getArgument($index)); + } - $method = new \ReflectionMethod( - 'Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\PropertyInfoPass', - 'findAndSortTaggedServices' + public function provideTags() + { + return array( + array(0, 'property_info.list_extractor'), + array(1, 'property_info.type_extractor'), + array(2, 'property_info.description_extractor'), + array(3, 'property_info.access_extractor'), ); - $method->setAccessible(true); - - $actual = $method->invoke($propertyInfoPass, 'tag', $container); - - $this->assertEquals($expected, $actual); } public function testReturningEmptyArrayWhenNoService() { - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder', array('findTaggedServiceIds')); + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->setMethods(array('findTaggedServiceIds'))->getMock(); $container ->expects($this->any()) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/SerializerPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/SerializerPassTest.php index 27f1636e8ce9d..8ad759e834592 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/SerializerPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/SerializerPassTest.php @@ -11,19 +11,23 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\SerializerPass; /** * Tests for the SerializerPass class. * + * @group legacy + * * @author Javier Lopez */ -class SerializerPassTest extends \PHPUnit_Framework_TestCase +class SerializerPassTest extends TestCase { public function testThrowExceptionWhenNoNormalizers() { - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder', array('hasDefinition', 'findTaggedServiceIds')); + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->setMethods(array('hasDefinition', 'findTaggedServiceIds'))->getMock(); $container->expects($this->once()) ->method('hasDefinition') @@ -35,7 +39,7 @@ public function testThrowExceptionWhenNoNormalizers() ->with('serializer.normalizer') ->will($this->returnValue(array())); - $this->setExpectedException('RuntimeException'); + $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('RuntimeException'); $serializerPass = new SerializerPass(); $serializerPass->process($container); @@ -43,11 +47,8 @@ public function testThrowExceptionWhenNoNormalizers() public function testThrowExceptionWhenNoEncoders() { - $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); - $container = $this->getMock( - 'Symfony\Component\DependencyInjection\ContainerBuilder', - array('hasDefinition', 'findTaggedServiceIds', 'getDefinition') - ); + $definition = $this->getMockBuilder('Symfony\Component\DependencyInjection\Definition')->getMock(); + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->setMethods(array('hasDefinition', 'findTaggedServiceIds', 'getDefinition'))->getMock(); $container->expects($this->once()) ->method('hasDefinition') @@ -61,11 +62,11 @@ public function testThrowExceptionWhenNoEncoders() array() )); - $container->expects($this->once()) + $container->expects($this->any()) ->method('getDefinition') ->will($this->returnValue($definition)); - $this->setExpectedException('RuntimeException'); + $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('RuntimeException'); $serializerPass = new SerializerPass(); $serializerPass->process($container); @@ -73,34 +74,22 @@ public function testThrowExceptionWhenNoEncoders() public function testServicesAreOrderedAccordingToPriority() { - $services = array( - 'n3' => array('tag' => array()), - 'n1' => array('tag' => array('priority' => 200)), - 'n2' => array('tag' => array('priority' => 100)), - ); - - $expected = array( - new Reference('n1'), - new Reference('n2'), - new Reference('n3'), - ); + $container = new ContainerBuilder(); - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder', array('findTaggedServiceIds')); - - $container->expects($this->any()) - ->method('findTaggedServiceIds') - ->will($this->returnValue($services)); + $definition = $container->register('serializer')->setArguments(array(null, null)); + $container->register('n2')->addTag('serializer.normalizer', array('priority' => 100))->addTag('serializer.encoder', array('priority' => 100)); + $container->register('n1')->addTag('serializer.normalizer', array('priority' => 200))->addTag('serializer.encoder', array('priority' => 200)); + $container->register('n3')->addTag('serializer.normalizer')->addTag('serializer.encoder'); $serializerPass = new SerializerPass(); + $serializerPass->process($container); - $method = new \ReflectionMethod( - 'Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\SerializerPass', - 'findAndSortTaggedServices' + $expected = array( + new Reference('n1'), + new Reference('n2'), + new Reference('n3'), ); - $method->setAccessible(true); - - $actual = $method->invoke($serializerPass, 'tag', $container); - - $this->assertEquals($expected, $actual); + $this->assertEquals($expected, $definition->getArgument(0)); + $this->assertEquals($expected, $definition->getArgument(1)); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/TranslatorPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/TranslatorPassTest.php index 83f70514d5456..a92f734eccc0f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/TranslatorPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/TranslatorPassTest.php @@ -11,38 +11,40 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; -use Symfony\Component\DependencyInjection\Reference; +use PHPUnit\Framework\TestCase; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslatorPass; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; -class TranslatorPassTest extends \PHPUnit_Framework_TestCase +class TranslatorPassTest extends TestCase { public function testValidCollector() { - $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); - $definition->expects($this->at(0)) - ->method('addMethodCall') - ->with('addLoader', array('xliff', new Reference('xliff'))); - $definition->expects($this->at(1)) - ->method('addMethodCall') - ->with('addLoader', array('xlf', new Reference('xliff'))); - - $container = $this->getMock( - 'Symfony\Component\DependencyInjection\ContainerBuilder', - array('hasDefinition', 'getDefinition', 'findTaggedServiceIds', 'findDefinition') - ); - $container->expects($this->any()) - ->method('hasDefinition') - ->will($this->returnValue(true)); - $container->expects($this->once()) - ->method('getDefinition') - ->will($this->returnValue($definition)); - $container->expects($this->once()) - ->method('findTaggedServiceIds') - ->will($this->returnValue(array('xliff' => array(array('alias' => 'xliff', 'legacy-alias' => 'xlf'))))); - $container->expects($this->once()) - ->method('findDefinition') - ->will($this->returnValue($this->getMock('Symfony\Component\DependencyInjection\Definition'))); + $loader = (new Definition()) + ->addTag('translation.loader', array('alias' => 'xliff', 'legacy-alias' => 'xlf')); + + $translator = (new Definition()) + ->setArguments(array(null, null, null, null)); + + $container = new ContainerBuilder(); + $container->setDefinition('translator.default', $translator); + $container->setDefinition('translation.loader', $loader); + $pass = new TranslatorPass(); $pass->process($container); + + $expected = (new Definition()) + ->addTag('translation.loader', array('alias' => 'xliff', 'legacy-alias' => 'xlf')) + ->addMethodCall('addLoader', array('xliff', new Reference('translation.loader'))) + ->addMethodCall('addLoader', array('xlf', new Reference('translation.loader'))) + ; + $this->assertEquals($expected, $loader); + + $this->assertSame(array('translation.loader' => array('xliff', 'xlf')), $translator->getArgument(3)); + + $expected = array('translation.loader' => new ServiceClosureArgument(new Reference('translation.loader'))); + $this->assertEquals($expected, $container->getDefinition((string) $translator->getArgument(0))->getArgument(0)); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/UnusedTagsPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/UnusedTagsPassTest.php index f354007bb982d..6c6f15899559c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/UnusedTagsPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/UnusedTagsPassTest.php @@ -11,28 +11,19 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; +use PHPUnit\Framework\TestCase; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\UnusedTagsPass; -class UnusedTagsPassTest extends \PHPUnit_Framework_TestCase +class UnusedTagsPassTest extends TestCase { public function testProcess() { $pass = new UnusedTagsPass(); - $formatter = $this->getMock('Symfony\Component\DependencyInjection\Compiler\LoggingFormatter'); - $formatter - ->expects($this->at(0)) - ->method('format') - ->with($pass, 'Tag "kenrel.event_subscriber" was defined on service(s) "foo", "bar", but was never used. Did you mean "kernel.event_subscriber"?') - ; - - $compiler = $this->getMock('Symfony\Component\DependencyInjection\Compiler\Compiler'); - $compiler->expects($this->once())->method('getLoggingFormatter')->will($this->returnValue($formatter)); - - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder', - array('findTaggedServiceIds', 'getCompiler', 'findUnusedTags', 'findTags') - ); - $container->expects($this->once())->method('getCompiler')->will($this->returnValue($compiler)); + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->setMethods(array('findTaggedServiceIds', 'findUnusedTags', 'findTags', 'log'))->getMock(); + $container->expects($this->once()) + ->method('log') + ->with($pass, 'Tag "kenrel.event_subscriber" was defined on service(s) "foo", "bar", but was never used. Did you mean "kernel.event_subscriber"?'); $container->expects($this->once()) ->method('findTags') ->will($this->returnValue(array('kenrel.event_subscriber'))); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index adfc46d14fb52..353e959c68ebf 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -11,10 +11,13 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection; +use PHPUnit\Framework\TestCase; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Configuration; +use Symfony\Bundle\FullStack; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\Config\Definition\Processor; -class ConfigurationTest extends \PHPUnit_Framework_TestCase +class ConfigurationTest extends TestCase { public function testDefaultConfig() { @@ -40,67 +43,6 @@ public function testDoNoDuplicateDefaultFormResources() $this->assertEquals(array('FrameworkBundle:Form'), $config['templating']['form']['resources']); } - /** - * @dataProvider getTestValidTrustedProxiesData - */ - public function testValidTrustedProxies($trustedProxies, $processedProxies) - { - $processor = new Processor(); - $configuration = new Configuration(true); - $config = $processor->processConfiguration($configuration, array(array( - 'secret' => 's3cr3t', - 'trusted_proxies' => $trustedProxies, - ))); - - $this->assertEquals($processedProxies, $config['trusted_proxies']); - } - - public function getTestValidTrustedProxiesData() - { - return array( - array(array('127.0.0.1'), array('127.0.0.1')), - array(array('::1'), array('::1')), - array(array('127.0.0.1', '::1'), array('127.0.0.1', '::1')), - array(null, array()), - array(false, array()), - array(array(), array()), - array(array('10.0.0.0/8'), array('10.0.0.0/8')), - array(array('::ffff:0:0/96'), array('::ffff:0:0/96')), - array(array('0.0.0.0/0'), array('0.0.0.0/0')), - ); - } - - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - */ - public function testInvalidTypeTrustedProxies() - { - $processor = new Processor(); - $configuration = new Configuration(true); - $processor->processConfiguration($configuration, array( - array( - 'secret' => 's3cr3t', - 'trusted_proxies' => 'Not an IP address', - ), - )); - } - - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - */ - public function testInvalidValueTrustedProxies() - { - $processor = new Processor(); - $configuration = new Configuration(true); - - $processor->processConfiguration($configuration, array( - array( - 'secret' => 's3cr3t', - 'trusted_proxies' => array('Not an IP address'), - ), - )); - } - public function testAssetsCanBeEnabled() { $processor = new Processor(); @@ -115,68 +57,80 @@ public function testAssetsCanBeEnabled() 'base_path' => '', 'base_urls' => array(), 'packages' => array(), + 'json_manifest_path' => null, ); $this->assertEquals($defaultConfig, $config['assets']); } /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - * @expectedExceptionMessage You cannot use both "version_strategy" and "version" at the same time under "assets". + * @dataProvider provideInvalidAssetConfigurationTests */ - public function testInvalidVersionStrategy() + public function testInvalidAssetsConfiguration(array $assetConfig, $expectedMessage) { + $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}( + InvalidConfigurationException::class, + $expectedMessage + ); + if (method_exists($this, 'expectExceptionMessage')) { + $this->expectExceptionMessage($expectedMessage); + } + $processor = new Processor(); $configuration = new Configuration(true); $processor->processConfiguration($configuration, array( - array( - 'assets' => array( - 'base_urls' => '//example.com', - 'version' => 1, - 'version_strategy' => 'foo', + array( + 'assets' => $assetConfig, ), - ), - )); + )); } - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - * @expectedExceptionMessage You cannot use both "version_strategy" and "version" at the same time under "assets" packages. - */ - public function testInvalidPackageVersionStrategy() + public function provideInvalidAssetConfigurationTests() { - $processor = new Processor(); - $configuration = new Configuration(true); - - $processor->processConfiguration($configuration, array( - array( - 'assets' => array( - 'base_urls' => '//example.com', - 'version' => 1, - 'packages' => array( - 'foo' => array( - 'base_urls' => '//example.com', - 'version' => 1, - 'version_strategy' => 'foo', - ), - ), + // helper to turn config into embedded package config + $createPackageConfig = function (array $packageConfig) { + return array( + 'base_urls' => '//example.com', + 'version' => 1, + 'packages' => array( + 'foo' => $packageConfig, ), - ), - )); + ); + }; + + $config = array( + 'version' => 1, + 'version_strategy' => 'foo', + ); + yield array($config, 'You cannot use both "version_strategy" and "version" at the same time under "assets".'); + yield array($createPackageConfig($config), 'You cannot use both "version_strategy" and "version" at the same time under "assets" packages.'); + + $config = array( + 'json_manifest_path' => '/foo.json', + 'version_strategy' => 'foo', + ); + yield array($config, 'You cannot use both "version_strategy" and "json_manifest_path" at the same time under "assets".'); + yield array($createPackageConfig($config), 'You cannot use both "version_strategy" and "json_manifest_path" at the same time under "assets" packages.'); + + $config = array( + 'json_manifest_path' => '/foo.json', + 'version' => '1', + ); + yield array($config, 'You cannot use both "version" and "json_manifest_path" at the same time under "assets".'); + yield array($createPackageConfig($config), 'You cannot use both "version" and "json_manifest_path" at the same time under "assets" packages.'); } protected static function getBundleDefaultConfig() { return array( 'http_method_override' => true, - 'trusted_proxies' => array(), 'ide' => null, 'default_locale' => 'en', 'csrf_protection' => array( 'enabled' => false, ), 'form' => array( - 'enabled' => false, + 'enabled' => !class_exists(FullStack::class), 'csrf_protection' => array( 'enabled' => null, // defaults to csrf_protection.enabled 'field_name' => '_token', @@ -200,17 +154,20 @@ protected static function getBundleDefaultConfig() ), ), 'translator' => array( - 'enabled' => false, + 'enabled' => !class_exists(FullStack::class), 'fallbacks' => array('en'), 'logging' => true, 'paths' => array(), ), 'validation' => array( - 'enabled' => false, - 'enable_annotations' => false, + 'enabled' => !class_exists(FullStack::class), + 'enable_annotations' => !class_exists(FullStack::class), 'static_method' => array('loadValidatorMetadata'), 'translation_domain' => 'validators', 'strict_email' => false, + 'mapping' => array( + 'paths' => array(), + ), ), 'annotations' => array( 'cache' => 'php_array', @@ -219,8 +176,9 @@ protected static function getBundleDefaultConfig() 'enabled' => true, ), 'serializer' => array( - 'enabled' => false, - 'enable_annotations' => false, + 'enabled' => !class_exists(FullStack::class), + 'enable_annotations' => !class_exists(FullStack::class), + 'mapping' => array('paths' => array()), ), 'property_access' => array( 'magic_call' => false, @@ -258,13 +216,14 @@ protected static function getBundleDefaultConfig() 'loaders' => array(), ), 'assets' => array( - 'enabled' => false, + 'enabled' => !class_exists(FullStack::class), 'version_strategy' => null, 'version' => null, 'version_format' => '%%s?%%s', 'base_path' => '', 'base_urls' => array(), 'packages' => array(), + 'json_manifest_path' => null, ), 'cache' => array( 'pools' => array(), @@ -272,12 +231,16 @@ protected static function getBundleDefaultConfig() 'system' => 'cache.adapter.system', 'directory' => '%kernel.cache_dir%/pools', 'default_redis_provider' => 'redis://localhost', + 'default_memcached_provider' => 'memcached://localhost', ), 'workflows' => array(), 'php_errors' => array( 'log' => true, 'throw' => true, ), + 'web_link' => array( + 'enabled' => !class_exists(FullStack::class), + ), ); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/Resources/config/validation.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/Resources/config/validation.xml new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/Resources/config/validation.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/Resources/config/validation.yml new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/src/CustomPathBundle.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/src/CustomPathBundle.php new file mode 100644 index 0000000000000..166b606a459e2 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/src/CustomPathBundle.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class CustomPathBundle extends Bundle +{ + public function getPath() + { + return __DIR__.'/..'; + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serialization.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serialization.xml new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serialization.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serialization.yml new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serializer_mapping/files/foo.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serializer_mapping/files/foo.xml new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serializer_mapping/files/foo.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serializer_mapping/files/foo.yml new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yaml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yaml new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yml new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation_mapping/files/foo.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation_mapping/files/foo.xml new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation_mapping/files/foo.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation_mapping/files/foo.yml new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yaml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yaml new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yml new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/TestBundle.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/TestBundle.php index b0c4548fa07de..2f090b2de8d53 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/TestBundle.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/TestBundle.php @@ -11,6 +11,8 @@ namespace Symfony\Bundle\FrameworkBundle\Tests; -class TestBundle extends \Symfony\Component\HttpKernel\Bundle\Bundle +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class TestBundle extends Bundle { } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/assets.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/assets.php index 3cfc5c1bc0cc4..dc6bf7bb8df55 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/assets.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/assets.php @@ -24,6 +24,9 @@ 'base_urls' => array('https://bar2.example.com'), 'version_strategy' => 'assets.custom_version_strategy', ), + 'json_manifest_strategy' => array( + 'json_manifest_path' => '/path/to/manifest.json', + ), ), ), )); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache_env_var.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache_env_var.php new file mode 100644 index 0000000000000..b93ade8f4c816 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache_env_var.php @@ -0,0 +1,9 @@ +setParameter('env(REDIS_URL)', 'redis://paas.com'); + +$container->loadFromExtension('framework', array( + 'cache' => array( + 'default_redis_provider' => '%env(REDIS_URL)%', + ), +)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php index 9173dd5615bda..58a4340e6979f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php @@ -10,7 +10,6 @@ ), ), 'http_method_override' => false, - 'trusted_proxies' => array('127.0.0.1', '10.0.0.1'), 'esi' => array( 'enabled' => true, ), diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/serializer_mapping.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/serializer_mapping.php new file mode 100644 index 0000000000000..4e437bf4e8e1c --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/serializer_mapping.php @@ -0,0 +1,15 @@ +loadFromExtension('framework', array( + 'annotations' => array('enabled' => true), + 'serializer' => array( + 'enable_annotations' => true, + 'mapping' => array( + 'paths' => array( + '%kernel.root_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/files', + '%kernel.root_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yml', + '%kernel.root_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yaml', + ), + ), + ), +)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/templating_php_translator_disabled.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/templating_php_translator_disabled.php new file mode 100644 index 0000000000000..4fb2aec557f67 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/templating_php_translator_disabled.php @@ -0,0 +1,8 @@ +loadFromExtension('framework', array( + 'translator' => false, + 'templating' => array( + 'engines' => array('php'), + ), +)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/templating_php_translator_enabled.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/templating_php_translator_enabled.php new file mode 100644 index 0000000000000..b8053c853b128 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/templating_php_translator_enabled.php @@ -0,0 +1,8 @@ +loadFromExtension('framework', array( + 'translator' => true, + 'templating' => array( + 'engines' => array('php'), + ), +)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_mapping.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_mapping.php new file mode 100644 index 0000000000000..51bbb90ef48a6 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_mapping.php @@ -0,0 +1,13 @@ +loadFromExtension('framework', array( + 'validation' => array( + 'mapping' => array( + 'paths' => array( + '%kernel.root_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/files', + '%kernel.root_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yml', + '%kernel.root_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yaml', + ), + ), + ), +)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/web_link.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/web_link.php new file mode 100644 index 0000000000000..990064cca934e --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/web_link.php @@ -0,0 +1,5 @@ +loadFromExtension('framework', array( + 'web_link' => array('enabled' => true), +)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_multiple_transitions_with_same_name.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_multiple_transitions_with_same_name.php new file mode 100644 index 0000000000000..2619a2dd4316a --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_multiple_transitions_with_same_name.php @@ -0,0 +1,49 @@ +loadFromExtension('framework', array( + 'workflows' => array( + 'article' => array( + 'type' => 'workflow', + 'marking_store' => array( + 'type' => 'multiple_state', + ), + 'supports' => array( + FrameworkExtensionTest::class, + ), + 'initial_place' => 'draft', + 'places' => array( + 'draft', + 'wait_for_journalist', + 'approved_by_journalist', + 'wait_for_spellchecker', + 'approved_by_spellchecker', + 'published', + ), + 'transitions' => array( + 'request_review' => array( + 'from' => 'draft', + 'to' => array('wait_for_journalist', 'wait_for_spellchecker'), + ), + 'journalist_approval' => array( + 'from' => 'wait_for_journalist', + 'to' => 'approved_by_journalist', + ), + 'spellchecker_approval' => array( + 'from' => 'wait_for_spellchecker', + 'to' => 'approved_by_spellchecker', + ), + 'publish' => array( + 'from' => array('approved_by_journalist', 'approved_by_spellchecker'), + 'to' => 'published', + ), + 'publish_editor_in_chief' => array( + 'name' => 'publish', + 'from' => 'draft', + 'to' => 'published', + ), + ), + ), + ), +)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_support_and_support_strategy.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_support_and_support_strategy.php new file mode 100644 index 0000000000000..062fdb9f0dba4 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_support_and_support_strategy.php @@ -0,0 +1,31 @@ +loadFromExtension('framework', array( + 'workflows' => array( + 'my_workflow' => array( + 'marking_store' => array( + 'type' => 'multiple_state', + ), + 'supports' => array( + FrameworkExtensionTest::class, + ), + 'support_strategy' => 'foobar', + 'places' => array( + 'first', + 'last', + ), + 'transitions' => array( + 'go' => array( + 'from' => array( + 'first', + ), + 'to' => array( + 'last', + ), + ), + ), + ), + ), +)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_without_support_and_support_strategy.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_without_support_and_support_strategy.php new file mode 100644 index 0000000000000..06948785e927c --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_without_support_and_support_strategy.php @@ -0,0 +1,27 @@ +loadFromExtension('framework', array( + 'workflows' => array( + 'my_workflow' => array( + 'marking_store' => array( + 'type' => 'multiple_state', + ), + 'places' => array( + 'first', + 'last', + ), + 'transitions' => array( + 'go' => array( + 'from' => array( + 'first', + ), + 'to' => array( + 'last', + ), + ), + ), + ), + ), +)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows.php index 854a858415e00..c527606561ee9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows.php @@ -88,5 +88,24 @@ ), ), ), + 'service_marking_store_workflow' => array( + 'type' => 'workflow', + 'marking_store' => array( + 'service' => 'workflow_service', + ), + 'supports' => array( + FrameworkExtensionTest::class, + ), + 'places' => array( + 'first', + 'last', + ), + 'transitions' => array( + 'go' => array( + 'from' => 'first', + 'to' => 'last', + ), + ), + ), ), )); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows_without_type.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows_without_type.php new file mode 100644 index 0000000000000..63e83170fca52 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows_without_type.php @@ -0,0 +1,26 @@ +loadFromExtension('framework', array( + 'workflows' => array( + 'missing_type' => array( + 'marking_store' => array( + 'service' => 'workflow_service', + ), + 'supports' => array( + \stdClass::class, + ), + 'places' => array( + 'first', + 'last', + ), + 'transitions' => array( + 'go' => array( + 'from' => 'first', + 'to' => 'last', + ), + ), + ), + ), +)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/assets.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/assets.xml index b99952b2923d3..a907a5b967f93 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/assets.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/assets.xml @@ -21,6 +21,7 @@ https://bar_version_strategy.example.com + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache_env_var.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache_env_var.xml new file mode 100644 index 0000000000000..8f03cb1784b35 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache_env_var.xml @@ -0,0 +1,17 @@ + + + + + redis://paas.com + + + + + %env(REDIS_URL)% + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml index 7c6b11ac44aa6..5c6a221c18489 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml @@ -6,7 +6,7 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/serializer_mapping.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/serializer_mapping.xml new file mode 100644 index 0000000000000..9fec09f0dbf0f --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/serializer_mapping.xml @@ -0,0 +1,17 @@ + + + + + + + + + %kernel.root_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/files + %kernel.root_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yml + %kernel.root_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yaml + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/templating_php_translator_disabled.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/templating_php_translator_disabled.xml new file mode 100644 index 0000000000000..72a78aaf0c090 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/templating_php_translator_disabled.xml @@ -0,0 +1,14 @@ + + + + + + + php + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/templating_php_translator_enabled.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/templating_php_translator_enabled.xml new file mode 100644 index 0000000000000..036afa38f7034 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/templating_php_translator_enabled.xml @@ -0,0 +1,14 @@ + + + + + + + php + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_mapping.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_mapping.xml new file mode 100644 index 0000000000000..c011269bd370d --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_mapping.xml @@ -0,0 +1,16 @@ + + + + + + + + %kernel.root_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/files + %kernel.root_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yml + %kernel.root_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yaml + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/web_link.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/web_link.xml new file mode 100644 index 0000000000000..a061f0b15b9ed --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/web_link.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_multiple_transitions_with_same_name.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_multiple_transitions_with_same_name.xml new file mode 100644 index 0000000000000..d52aed8c95234 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_multiple_transitions_with_same_name.xml @@ -0,0 +1,46 @@ + + + + + + + + a + a + + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + draft + wait_for_journalist + approved_by_journalist + wait_for_spellchecker + approved_by_spellchecker + published + + draft + wait_for_journalist + wait_for_spellchecker + + + wait_for_journalist + approved_by_journalist + + + wait_for_spellchecker + approved_by_spellchecker + + + approved_by_journalist + approved_by_spellchecker + published + + + draft + published + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_support_and_support_strategy.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_support_and_support_strategy.xml new file mode 100644 index 0000000000000..92e26ff327d94 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_support_and_support_strategy.xml @@ -0,0 +1,21 @@ + + + + + + + + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + first + last + + a + a + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_without_support_and_support_strategy.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_without_support_and_support_strategy.xml new file mode 100644 index 0000000000000..14bb287cca489 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_without_support_and_support_strategy.xml @@ -0,0 +1,20 @@ + + + + + + + + first + last + + a + a + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml index 924ff75111ca8..be065c4558858 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml @@ -79,5 +79,16 @@ review + + + + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + first + last + + first + last + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows_without_type.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows_without_type.xml new file mode 100644 index 0000000000000..2e6ebad552b74 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows_without_type.xml @@ -0,0 +1,21 @@ + + + + + + + + stdClass + first + last + + first + last + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/assets.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/assets.yml index 73cd9234e7060..a1679e389ddbf 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/assets.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/assets.yml @@ -17,3 +17,5 @@ framework: bar_version_strategy: base_urls: ["https://bar_version_strategy.example.com"] version_strategy: assets.custom_version_strategy + json_manifest_strategy: + json_manifest_path: '/path/to/manifest.json' diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache_env_var.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache_env_var.yml new file mode 100644 index 0000000000000..1d9ce5f7f02f7 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache_env_var.yml @@ -0,0 +1,6 @@ +parameters: + env(REDIS_URL): redis://paas.com + +framework: + cache: + default_redis_provider: "%env(REDIS_URL)%" diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml index 237654880c984..f471372097c15 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml @@ -6,7 +6,6 @@ framework: csrf_protection: field_name: _csrf http_method_override: false - trusted_proxies: ['127.0.0.1', '10.0.0.1'] esi: enabled: true profiler: diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/serializer_mapping.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/serializer_mapping.yml new file mode 100644 index 0000000000000..b977dc89be52f --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/serializer_mapping.yml @@ -0,0 +1,10 @@ +framework: + annotations: + enabled: true + serializer: + enable_annotations: true + mapping: + paths: + - "%kernel.root_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/files" + - "%kernel.root_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yml" + - "%kernel.root_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yaml" diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/templating_php_translator_disabled.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/templating_php_translator_disabled.yml new file mode 100644 index 0000000000000..fe0f3e83b5683 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/templating_php_translator_disabled.yml @@ -0,0 +1,4 @@ +framework: + translator: false + templating: + engines: [php] diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/templating_php_translator_enabled.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/templating_php_translator_enabled.yml new file mode 100644 index 0000000000000..0991a2007d77f --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/templating_php_translator_enabled.yml @@ -0,0 +1,4 @@ +framework: + translator: true + templating: + engines: [php] diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_mapping.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_mapping.yml new file mode 100644 index 0000000000000..7d79bc9a1c477 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_mapping.yml @@ -0,0 +1,7 @@ +framework: + validation: + mapping: + paths: + - "%kernel.root_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/files" + - "%kernel.root_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yml" + - "%kernel.root_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yaml" diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/web_link.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/web_link.yml new file mode 100644 index 0000000000000..4276aacbe21c1 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/web_link.yml @@ -0,0 +1,3 @@ +framework: + web_link: + enabled: true diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_multiple_transitions_with_same_name.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_multiple_transitions_with_same_name.yml new file mode 100644 index 0000000000000..36d00de46501c --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_multiple_transitions_with_same_name.yml @@ -0,0 +1,33 @@ +framework: + workflows: + article: + type: workflow + marking_store: + type: multiple_state + supports: + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + initial_place: draft + places: + - draft + - wait_for_journalist + - approved_by_journalist + - wait_for_spellchecker + - approved_by_spellchecker + - published + transitions: + request_review: + from: [draft] + to: [wait_for_journalist, wait_for_spellchecker] + journalist_approval: + from: [wait_for_journalist] + to: [approved_by_journalist] + spellchecker_approval: + from: [wait_for_spellchecker] + to: [approved_by_spellchecker] + publish: + from: [approved_by_journalist, approved_by_spellchecker] + to: [published] + publish_editor_in_chief: + name: publish + from: [draft] + to: [published] diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_support_and_support_strategy.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_support_and_support_strategy.yml new file mode 100644 index 0000000000000..743708485ce65 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_support_and_support_strategy.yml @@ -0,0 +1,17 @@ +framework: + workflows: + my_workflow: + marking_store: + type: multiple_state + supports: + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + support_strategy: foobar + places: + - first + - last + transitions: + go: + from: + - first + to: + - last diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_without_support_and_support_strategy.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_without_support_and_support_strategy.yml new file mode 100644 index 0000000000000..6dc848d936b21 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_without_support_and_support_strategy.yml @@ -0,0 +1,14 @@ +framework: + workflows: + my_workflow: + marking_store: + type: multiple_state + places: + - first + - last + transitions: + go: + from: + - first + to: + - last diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml index a933db5100fc8..36b84f71e4582 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml @@ -63,3 +63,18 @@ framework: reopen: from: closed to: review + service_marking_store_workflow: + type: workflow + marking_store: + service: workflow_service + supports: + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + places: + - first + - last + transitions: + go: + from: + - first + to: + - last diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows_without_type.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows_without_type.yml new file mode 100644 index 0000000000000..41b81683ba445 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows_without_type.yml @@ -0,0 +1,7 @@ +framework: + workflows: + missing_type: + supports: stdClass + places: [ first, second ] + transitions: + go: {from: first, to: last } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index c1ee84685654f..4f86c85d4f8fb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -11,26 +11,37 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection; +use Doctrine\Common\Annotations\Annotation; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslatorPass; +use Symfony\Bundle\FullStack; use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddAnnotationsCachedReaderPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension; +use Symfony\Component\Cache\Adapter\AdapterInterface; use Symfony\Component\Cache\Adapter\ApcuAdapter; +use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Cache\Adapter\ChainAdapter; use Symfony\Component\Cache\Adapter\DoctrineAdapter; use Symfony\Component\Cache\Adapter\FilesystemAdapter; use Symfony\Component\Cache\Adapter\ProxyAdapter; use Symfony\Component\Cache\Adapter\RedisAdapter; +use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\Loader\ClosureLoader; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\PropertyAccess\PropertyAccessor; +use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; +use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Mapping\Factory\CacheClassMetadataFactory; use Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader; use Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader; use Symfony\Component\Serializer\Normalizer\DataUriNormalizer; use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer; use Symfony\Component\Serializer\Normalizer\JsonSerializableNormalizer; +use Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass; abstract class FrameworkExtensionTest extends TestCase { @@ -67,6 +78,32 @@ public function testPropertyAccessWithOverriddenValues() $this->assertTrue($def->getArgument(1)); } + public function testPropertyAccessCache() + { + $container = $this->createContainerFromFile('property_accessor'); + + if (!method_exists(PropertyAccessor::class, 'createCache')) { + return $this->assertFalse($container->hasDefinition('cache.property_access')); + } + + $cache = $container->getDefinition('cache.property_access'); + $this->assertSame(array(PropertyAccessor::class, 'createCache'), $cache->getFactory(), 'PropertyAccessor::createCache() should be used in non-debug mode'); + $this->assertSame(AdapterInterface::class, $cache->getClass()); + } + + public function testPropertyAccessCacheWithDebug() + { + $container = $this->createContainerFromFile('property_accessor', array('kernel.debug' => true)); + + if (!method_exists(PropertyAccessor::class, 'createCache')) { + return $this->assertFalse($container->hasDefinition('cache.property_access')); + } + + $cache = $container->getDefinition('cache.property_access'); + $this->assertNull($cache->getFactory()); + $this->assertSame(ArrayAdapter::class, $cache->getClass(), 'ArrayAdapter should be used in debug mode'); + } + /** * @expectedException \LogicException * @expectedExceptionMessage CSRF protection needs sessions to be enabled. @@ -83,13 +120,6 @@ public function testCsrfProtectionForFormsEnablesCsrfProtectionAutomatically() $this->assertTrue($container->hasDefinition('security.csrf.token_manager')); } - public function testProxies() - { - $container = $this->createContainerFromFile('full'); - - $this->assertEquals(array('127.0.0.1', '10.0.0.1'), $container->getParameter('kernel.trusted_proxies')); - } - public function testHttpMethodOverride() { $container = $this->createContainerFromFile('full'); @@ -165,6 +195,25 @@ public function testWorkflows() $this->assertSame(array('workflow.definition' => array(array('name' => 'pull_request', 'type' => 'state_machine', 'marking_store' => 'single_state'))), $stateMachineDefinition->getTags()); $this->assertCount(9, $stateMachineDefinition->getArgument(1)); $this->assertSame('start', $stateMachineDefinition->getArgument(2)); + + $serviceMarkingStoreWorkflowDefinition = $container->getDefinition('workflow.service_marking_store_workflow'); + /** @var Reference $markingStoreRef */ + $markingStoreRef = $serviceMarkingStoreWorkflowDefinition->getArgument(1); + $this->assertInstanceOf(Reference::class, $markingStoreRef); + $this->assertEquals('workflow_service', (string) $markingStoreRef); + + $this->assertTrue($container->hasDefinition('workflow.registry', 'Workflow registry is registered as a service')); + $registryDefinition = $container->getDefinition('workflow.registry'); + $this->assertGreaterThan(0, count($registryDefinition->getMethodCalls())); + } + + /** + * @group legacy + * @expectedDeprecation The "type" option of the "framework.workflows.missing_type" configuration entry must be defined since Symfony 3.3. The default value will be "state_machine" in Symfony 4.0. + */ + public function testDeprecatedWorkflowMissingType() + { + $container = $this->createContainerFromFile('workflows_without_type'); } /** @@ -176,6 +225,24 @@ public function testWorkflowCannotHaveBothTypeAndService() $this->createContainerFromFile('workflow_with_type_and_service'); } + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage "supports" and "support_strategy" cannot be used together. + */ + public function testWorkflowCannotHaveBothSupportsAndSupportStrategy() + { + $this->createContainerFromFile('workflow_with_support_and_support_strategy'); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage "supports" or "support_strategy" should be configured. + */ + public function testWorkflowShouldHaveOneOfSupportsAndSupportStrategy() + { + $this->createContainerFromFile('workflow_without_support_and_support_strategy'); + } + /** * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException * @expectedExceptionMessage "arguments" and "service" cannot be used together. @@ -185,6 +252,29 @@ public function testWorkflowCannotHaveBothArgumentsAndService() $this->createContainerFromFile('workflow_with_arguments_and_service'); } + public function testWorkflowMultipleTransitionsWithSameName() + { + $container = $this->createContainerFromFile('workflow_with_multiple_transitions_with_same_name'); + + $this->assertTrue($container->hasDefinition('workflow.article', 'Workflow is registered as a service')); + $this->assertTrue($container->hasDefinition('workflow.article.definition', 'Workflow definition is registered as a service')); + + $workflowDefinition = $container->getDefinition('workflow.article.definition'); + + $transitions = $workflowDefinition->getArgument(1); + + $this->assertCount(5, $transitions); + + $this->assertSame('request_review', $transitions[0]->getArgument(0)); + $this->assertSame('journalist_approval', $transitions[1]->getArgument(0)); + $this->assertSame('spellchecker_approval', $transitions[2]->getArgument(0)); + $this->assertSame('publish', $transitions[3]->getArgument(0)); + $this->assertSame('publish', $transitions[4]->getArgument(0)); + + $this->assertSame(array('approved_by_journalist', 'approved_by_spellchecker'), $transitions[3]->getArgument(1)); + $this->assertSame(array('draft'), $transitions[4]->getArgument(1)); + } + public function testRouter() { $container = $this->createContainerFromFile('full'); @@ -289,27 +379,32 @@ public function testAssets() $packages = $container->getDefinition('assets.packages'); // default package - $defaultPackage = $container->getDefinition($packages->getArgument(0)); + $defaultPackage = $container->getDefinition((string) $packages->getArgument(0)); $this->assertUrlPackage($container, $defaultPackage, array('http://cdn.example.com'), 'SomeVersionScheme', '%%s?version=%%s'); // packages $packages = $packages->getArgument(1); - $this->assertCount(5, $packages); + $this->assertCount(6, $packages); - $package = $container->getDefinition($packages['images_path']); + $package = $container->getDefinition((string) $packages['images_path']); $this->assertPathPackage($container, $package, '/foo', 'SomeVersionScheme', '%%s?version=%%s'); - $package = $container->getDefinition($packages['images']); + $package = $container->getDefinition((string) $packages['images']); $this->assertUrlPackage($container, $package, array('http://images1.example.com', 'http://images2.example.com'), '1.0.0', '%%s?version=%%s'); - $package = $container->getDefinition($packages['foo']); + $package = $container->getDefinition((string) $packages['foo']); $this->assertPathPackage($container, $package, '', '1.0.0', '%%s-%%s'); - $package = $container->getDefinition($packages['bar']); + $package = $container->getDefinition((string) $packages['bar']); $this->assertUrlPackage($container, $package, array('https://bar2.example.com'), 'SomeVersionScheme', '%%s?version=%%s'); - $package = $container->getDefinition($packages['bar_version_strategy']); + $package = $container->getDefinition((string) $packages['bar_version_strategy']); $this->assertEquals('assets.custom_version_strategy', (string) $package->getArgument(1)); + + $package = $container->getDefinition((string) $packages['json_manifest_strategy']); + $versionStrategy = $container->getDefinition((string) $package->getArgument(1)); + $this->assertEquals('assets.json_manifest_version_strategy', $versionStrategy->getParent()); + $this->assertEquals('/path/to/manifest.json', $versionStrategy->getArgument(0)); } public function testAssetsDefaultVersionStrategyAsService() @@ -318,18 +413,24 @@ public function testAssetsDefaultVersionStrategyAsService() $packages = $container->getDefinition('assets.packages'); // default package - $defaultPackage = $container->getDefinition($packages->getArgument(0)); + $defaultPackage = $container->getDefinition((string) $packages->getArgument(0)); $this->assertEquals('assets.custom_version_strategy', (string) $defaultPackage->getArgument(1)); } + public function testWebLink() + { + $container = $this->createContainerFromFile('web_link'); + $this->assertTrue($container->hasDefinition('web_link.add_link_header_listener')); + } + public function testTranslator() { $container = $this->createContainerFromFile('full'); $this->assertTrue($container->hasDefinition('translator.default'), '->registerTranslatorConfiguration() loads translation.xml'); $this->assertEquals('translator.default', (string) $container->getAlias('translator'), '->registerTranslatorConfiguration() redefines translator service from identity to real translator'); - $options = $container->getDefinition('translator.default')->getArgument(3); + $options = $container->getDefinition('translator.default')->getArgument(4); - $files = array_map(function ($resource) { return realpath($resource); }, $options['resource_files']['en']); + $files = array_map('realpath', $options['resource_files']['en']); $ref = new \ReflectionClass('Symfony\Component\Validator\Validation'); $this->assertContains( strtr(dirname($ref->getFileName()).'/Resources/translations/validators.en.xlf', '/', DIRECTORY_SEPARATOR), @@ -366,6 +467,20 @@ public function testTranslatorMultipleFallbacks() $this->assertEquals(array('en', 'fr'), $calls[1][1][0]); } + public function testTranslatorHelperIsRegisteredWhenTranslatorIsEnabled() + { + $container = $this->createContainerFromFile('templating_php_translator_enabled'); + + $this->assertTrue($container->has('templating.helper.translator')); + } + + public function testTranslatorHelperIsNotRegisteredWhenTranslatorIsDisabled() + { + $container = $this->createContainerFromFile('templating_php_translator_disabled'); + + $this->assertFalse($container->has('templating.helper.translator')); + } + /** * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException */ @@ -385,7 +500,9 @@ public function testValidation() $calls = $container->getDefinition('validator.builder')->getMethodCalls(); - $this->assertCount(6, $calls); + $annotations = !class_exists(FullStack::class) && class_exists(Annotation::class); + + $this->assertCount($annotations ? 7 : 6, $calls); $this->assertSame('setConstraintValidatorFactory', $calls[0][0]); $this->assertEquals(array(new Reference('validator.validator_factory')), $calls[0][1]); $this->assertSame('setTranslator', $calls[1][0]); @@ -394,10 +511,14 @@ public function testValidation() $this->assertSame(array('%validator.translation_domain%'), $calls[2][1]); $this->assertSame('addXmlMappings', $calls[3][0]); $this->assertSame(array($xmlMappings), $calls[3][1]); - $this->assertSame('addMethodMapping', $calls[4][0]); - $this->assertSame(array('loadValidatorMetadata'), $calls[4][1]); - $this->assertSame('setMetadataCache', $calls[5][0]); - $this->assertEquals(array(new Reference('validator.mapping.cache.symfony')), $calls[5][1]); + $i = 3; + if ($annotations) { + $this->assertSame('enableAnnotationMapping', $calls[++$i][0]); + } + $this->assertSame('addMethodMapping', $calls[++$i][0]); + $this->assertSame(array('loadValidatorMetadata'), $calls[$i][1]); + $this->assertSame('setMetadataCache', $calls[++$i][0]); + $this->assertEquals(array(new Reference('validator.mapping.cache.symfony')), $calls[$i][1]); } public function testValidationService() @@ -412,12 +533,15 @@ public function testAnnotations() $container = $this->createContainerFromFile('full'); $this->assertEquals($container->getParameter('kernel.cache_dir').'/annotations', $container->getDefinition('annotations.filesystem_cache')->getArgument(0)); - $this->assertSame('annotations.cached_reader', (string) $container->getAlias('annotation_reader')); $this->assertSame('annotations.filesystem_cache', (string) $container->getDefinition('annotations.cached_reader')->getArgument(1)); } public function testFileLinkFormat() { + if (ini_get('xdebug.file_link_format') || get_cfg_var('xdebug.file_link_format')) { + $this->markTestSkipped('A custom file_link_format is defined.'); + } + $container = $this->createContainerFromFile('full'); $this->assertEquals('file%link%format', $container->getParameter('debug.file_link_format')); @@ -444,7 +568,8 @@ public function testValidationPaths() require_once __DIR__.'/Fixtures/TestBundle/TestBundle.php'; $container = $this->createContainerFromFile('validation_annotations', array( - 'kernel.bundles' => array('TestBundle' => 'Symfony\Bundle\FrameworkBundle\Tests\TestBundle'), + 'kernel.bundles' => array('TestBundle' => 'Symfony\\Bundle\\FrameworkBundle\\Tests\\TestBundle'), + 'kernel.bundles_metadata' => array('TestBundle' => array('namespace' => 'Symfony\\Bundle\\FrameworkBundle\\Tests', 'parent' => null, 'path' => __DIR__.'/Fixtures/TestBundle')), )); $calls = $container->getDefinition('validator.builder')->getMethodCalls(); @@ -474,19 +599,68 @@ public function testValidationPaths() $this->assertStringEndsWith('TestBundle/Resources/config/validation.yml', $yamlMappings[0]); } + public function testValidationPathsUsingCustomBundlePath() + { + require_once __DIR__.'/Fixtures/CustomPathBundle/src/CustomPathBundle.php'; + + $container = $this->createContainerFromFile('validation_annotations', array( + 'kernel.bundles' => array('CustomPathBundle' => 'Symfony\\Bundle\\FrameworkBundle\\Tests\\CustomPathBundle'), + 'kernel.bundles_metadata' => array('TestBundle' => array('namespace' => 'Symfony\\Bundle\\FrameworkBundle\\Tests', 'parent' => null, 'path' => __DIR__.'/Fixtures/CustomPathBundle')), + )); + + $calls = $container->getDefinition('validator.builder')->getMethodCalls(); + $xmlMappings = $calls[3][1][0]; + $this->assertCount(2, $xmlMappings); + + try { + // Testing symfony/symfony + $this->assertStringEndsWith('Component'.DIRECTORY_SEPARATOR.'Form/Resources/config/validation.xml', $xmlMappings[0]); + } catch (\Exception $e) { + // Testing symfony/framework-bundle with deps=high + $this->assertStringEndsWith('symfony'.DIRECTORY_SEPARATOR.'form/Resources/config/validation.xml', $xmlMappings[0]); + } + $this->assertStringEndsWith('CustomPathBundle/Resources/config/validation.xml', $xmlMappings[1]); + + $yamlMappings = $calls[4][1][0]; + $this->assertCount(1, $yamlMappings); + $this->assertStringEndsWith('CustomPathBundle/Resources/config/validation.yml', $yamlMappings[0]); + } + public function testValidationNoStaticMethod() { $container = $this->createContainerFromFile('validation_no_static_method'); $calls = $container->getDefinition('validator.builder')->getMethodCalls(); - $this->assertCount(5, $calls); + $annotations = !class_exists(FullStack::class) && class_exists(Annotation::class); + + $this->assertCount($annotations ? 6 : 5, $calls); $this->assertSame('addXmlMappings', $calls[3][0]); - $this->assertSame('setMetadataCache', $calls[4][0]); - $this->assertEquals(array(new Reference('validator.mapping.cache.symfony')), $calls[4][1]); + $i = 3; + if ($annotations) { + $this->assertSame('enableAnnotationMapping', $calls[++$i][0]); + } + $this->assertSame('setMetadataCache', $calls[++$i][0]); + $this->assertEquals(array(new Reference('validator.mapping.cache.symfony')), $calls[$i][1]); // no cache, no annotations, no static methods } + public function testValidationMapping() + { + $container = $this->createContainerFromFile('validation_mapping'); + + $calls = $container->getDefinition('validator.builder')->getMethodCalls(); + + $this->assertSame('addXmlMappings', $calls[3][0]); + $this->assertCount(2, $calls[3][1][0]); + + $this->assertSame('addYamlMappings', $calls[4][0]); + $this->assertCount(3, $calls[4][1][0]); + $this->assertContains('foo.yml', $calls[4][1][0][0]); + $this->assertContains('validation.yml', $calls[4][1][0][1]); + $this->assertContains('validation.yaml', $calls[4][1][0][2]); + } + public function testFormsCanBeEnabledWithoutCsrfProtection() { $container = $this->createContainerFromFile('form_no_csrf'); @@ -516,7 +690,7 @@ public function testStopwatchEnabledWithDebugModeDisabled() public function testSerializerDisabled() { $container = $this->createContainerFromFile('default_config'); - $this->assertFalse($container->has('serializer')); + $this->assertSame(!class_exists(FullStack::class) && class_exists(Serializer::class), $container->has('serializer')); } public function testSerializerEnabled() @@ -636,6 +810,28 @@ public function testDeprecatedSerializerCacheOption() $this->assertEquals(new Reference('foo'), $cache); } + public function testSerializerMapping() + { + $container = $this->createContainerFromFile('serializer_mapping', array('kernel.bundles_metadata' => array('TestBundle' => array('namespace' => 'Symfony\\Bundle\\FrameworkBundle\\Tests', 'path' => __DIR__.'/Fixtures/TestBundle', 'parent' => null)))); + $configDir = __DIR__.'/Fixtures/TestBundle/Resources/config'; + $expectedLoaders = array( + new Definition(AnnotationLoader::class, array(new Reference('annotation_reader'))), + new Definition(XmlFileLoader::class, array($configDir.'/serialization.xml')), + new Definition(YamlFileLoader::class, array($configDir.'/serialization.yml')), + new Definition(XmlFileLoader::class, array($configDir.'/serializer_mapping/files/foo.xml')), + new Definition(YamlFileLoader::class, array($configDir.'/serializer_mapping/files/foo.yml')), + new Definition(YamlFileLoader::class, array($configDir.'/serializer_mapping/serialization.yml')), + new Definition(YamlFileLoader::class, array($configDir.'/serializer_mapping/serialization.yaml')), + ); + + foreach ($expectedLoaders as $definition) { + $definition->setPublic(false); + } + + $loaders = $container->getDefinition('serializer.mapping.chain_loader')->getArgument(0); + $this->assertEquals(sort($expectedLoaders), sort($loaders)); + } + public function testAssetHelperWhenAssetsAreEnabled() { $container = $this->createContainerFromFile('full'); @@ -692,6 +888,46 @@ public function testPropertyInfoEnabled() $this->assertTrue($container->has('property_info')); } + public function testEventDispatcherService() + { + $container = $this->createContainer(array('kernel.charset' => 'UTF-8', 'kernel.secret' => 'secret')); + $container->registerExtension(new FrameworkExtension()); + $this->loadFromFile($container, 'default_config'); + $container + ->register('foo', \stdClass::class) + ->setProperty('dispatcher', new Reference('event_dispatcher')); + $container->compile(); + $this->assertInstanceOf(EventDispatcherInterface::class, $container->get('foo')->dispatcher); + } + + public function testCacheDefaultRedisProvider() + { + $container = $this->createContainerFromFile('cache'); + + $redisUrl = 'redis://localhost'; + $providerId = md5($redisUrl); + + $this->assertTrue($container->hasDefinition($providerId)); + + $url = $container->getDefinition($providerId)->getArgument(0); + + $this->assertSame($redisUrl, $url); + } + + public function testCacheDefaultRedisProviderWithEnvVar() + { + $container = $this->createContainerFromFile('cache_env_var'); + + $redisUrl = 'redis://paas.com'; + $providerId = md5($redisUrl); + + $this->assertTrue($container->hasDefinition($providerId)); + + $url = $container->getDefinition($providerId)->getArgument(0); + + $this->assertSame($redisUrl, $url); + } + public function testCachePoolServices() { $container = $this->createContainerFromFile('cache'); @@ -707,6 +943,7 @@ protected function createContainer(array $data = array()) { return new ContainerBuilder(new ParameterBag(array_merge(array( 'kernel.bundles' => array('FrameworkBundle' => 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle'), + 'kernel.bundles_metadata' => array('FrameworkBundle' => array('namespace' => 'Symfony\\Bundle\\FrameworkBundle', 'path' => __DIR__.'/../..', 'parent' => null)), 'kernel.cache_dir' => __DIR__, 'kernel.debug' => false, 'kernel.environment' => 'test', @@ -730,6 +967,7 @@ protected function createContainerFromFile($file, $data = array(), $resetCompile $container->getCompilerPassConfig()->setOptimizationPasses(array()); $container->getCompilerPassConfig()->setRemovingPasses(array()); } + $container->getCompilerPassConfig()->setBeforeRemovingPasses(array(new AddAnnotationsCachedReaderPass(), new AddConstraintValidatorsPass(), new TranslatorPass())); $container->compile(); return self::$containerCache[$cacheKey] = $container; @@ -749,14 +987,14 @@ protected function createContainerFromClosure($closure, $data = array()) return $container; } - private function assertPathPackage(ContainerBuilder $container, DefinitionDecorator $package, $basePath, $version, $format) + private function assertPathPackage(ContainerBuilder $container, ChildDefinition $package, $basePath, $version, $format) { $this->assertEquals('assets.path_package', $package->getParent()); $this->assertEquals($basePath, $package->getArgument(0)); $this->assertVersionStrategy($container, $package->getArgument(1), $version, $format); } - private function assertUrlPackage(ContainerBuilder $container, DefinitionDecorator $package, $baseUrls, $version, $format) + private function assertUrlPackage(ContainerBuilder $container, ChildDefinition $package, $baseUrls, $version, $format) { $this->assertEquals('assets.url_package', $package->getParent()); $this->assertEquals($baseUrls, $package->getArgument(0)); @@ -765,7 +1003,7 @@ private function assertUrlPackage(ContainerBuilder $container, DefinitionDecorat private function assertVersionStrategy(ContainerBuilder $container, Reference $reference, $version, $format) { - $versionStrategy = $container->getDefinition($reference); + $versionStrategy = $container->getDefinition((string) $reference); if (null === $version) { $this->assertEquals('assets.empty_version_strategy', (string) $reference); } else { @@ -781,7 +1019,7 @@ private function assertCachePoolServiceDefinitionIsCreated(ContainerBuilder $con $poolDefinition = $container->getDefinition($id); - $this->assertInstanceOf(DefinitionDecorator::class, $poolDefinition, sprintf('Cache pool "%s" is based on an abstract cache pool.', $id)); + $this->assertInstanceOf(ChildDefinition::class, $poolDefinition, sprintf('Cache pool "%s" is based on an abstract cache pool.', $id)); $this->assertTrue($poolDefinition->hasTag('cache.pool'), sprintf('Service definition "%s" is tagged with the "cache.pool" tag.', $id)); $this->assertFalse($poolDefinition->isAbstract(), sprintf('Service definition "%s" is not abstract.', $id)); @@ -794,7 +1032,7 @@ private function assertCachePoolServiceDefinitionIsCreated(ContainerBuilder $con do { $parentId = $parentDefinition->getParent(); $parentDefinition = $container->findDefinition($parentId); - } while ($parentDefinition instanceof DefinitionDecorator); + } while ($parentDefinition instanceof ChildDefinition); switch ($adapter) { case 'cache.adapter.apcu': diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_1.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_1.json new file mode 100644 index 0000000000000..efcf34d06aa77 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_1.json @@ -0,0 +1,20 @@ +[ + { + "service": "service_1", + "public": true + }, + { + "class": "Full\\Qualified\\Class1", + "public": true, + "synthetic": false, + "lazy": true, + "shared": true, + "abstract": true, + "autowire": false, + "autoconfigure": false, + "file": null, + "factory_class": "Full\\Qualified\\FactoryClass", + "factory_method": "get", + "tags": [] + } +] diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_1.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_1.md new file mode 100644 index 0000000000000..ac56c0d4a4aff --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_1.md @@ -0,0 +1,17 @@ +### alias_1 + +- Service: `service_1` +- Public: yes + +### service_1 + +- Class: `Full\Qualified\Class1` +- Public: yes +- Synthetic: no +- Lazy: yes +- Shared: yes +- Abstract: yes +- Autowired: no +- Autoconfigured: no +- Factory Class: `Full\Qualified\FactoryClass` +- Factory Method: `get` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_1.txt b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_1.txt new file mode 100644 index 0000000000000..75347e1a0e431 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_1.txt @@ -0,0 +1,20 @@ + // This service is an alias for the service service_1 + +Information for Service "service_1" +=================================== + + ---------------- ----------------------------- +  Option   Value  + ---------------- ----------------------------- + Service ID service_1 + Class Full\Qualified\Class1 + Tags - + Public yes + Synthetic no + Lazy yes + Shared yes + Abstract yes + Autowired no + Factory Class Full\Qualified\FactoryClass + Factory Method get + ---------------- ----------------------------- diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_1.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_1.xml new file mode 100644 index 0000000000000..19f5a04344fef --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_1.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_2.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_2.json new file mode 100644 index 0000000000000..03780e3eebe7a --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_2.json @@ -0,0 +1,41 @@ +[ + { + "service": "service_2", + "public": false + }, + { + "class": "Full\\Qualified\\Class2", + "public": false, + "synthetic": true, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "file": "\/path\/to\/file", + "factory_service": "factory.service", + "factory_method": "get", + "calls": [ + "setMailer" + ], + "tags": [ + { + "name": "tag1", + "parameters": { + "attr1": "val1", + "attr2": "val2" + } + }, + { + "name": "tag1", + "parameters": { + "attr3": "val3" + } + }, + { + "name": "tag2", + "parameters": [] + } + ] + } +] diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_2.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_2.md new file mode 100644 index 0000000000000..406c5dcada7a4 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_2.md @@ -0,0 +1,25 @@ +### alias_2 + +- Service: `service_2` +- Public: no + +### service_2 + +- Class: `Full\Qualified\Class2` +- Public: no +- Synthetic: yes +- Lazy: no +- Shared: yes +- Abstract: no +- Autowired: no +- Autoconfigured: no +- File: `/path/to/file` +- Factory Service: `factory.service` +- Factory Method: `get` +- Call: `setMailer` +- Tag: `tag1` + - Attr1: val1 + - Attr2: val2 +- Tag: `tag1` + - Attr3: val3 +- Tag: `tag2` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_2.txt b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_2.txt new file mode 100644 index 0000000000000..4d11ab8eef220 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_2.txt @@ -0,0 +1,24 @@ + // This service is an alias for the service service_2 + +Information for Service "service_2" +=================================== + + ----------------- --------------------------------- +  Option   Value  + ----------------- --------------------------------- + Service ID service_2 + Class Full\Qualified\Class2 + Tags tag1 (attr1: val1, attr2: val2)  + tag1 (attr3: val3)  + tag2 + Calls setMailer + Public no + Synthetic yes + Lazy no + Shared yes + Abstract no + Autowired no + Required File /path/to/file + Factory Service factory.service + Factory Method get + ----------------- --------------------------------- diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_2.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_2.xml new file mode 100644 index 0000000000000..3c15460beebe8 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_2.xml @@ -0,0 +1,18 @@ + + + + + + + + + + val1 + val2 + + + val3 + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.json new file mode 100644 index 0000000000000..c304b33ac2283 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.json @@ -0,0 +1,91 @@ +{ + "definitions": { + "definition_1": { + "class": "Full\\Qualified\\Class1", + "public": true, + "synthetic": false, + "lazy": true, + "shared": true, + "abstract": true, + "autowire": false, + "autoconfigure": false, + "arguments": [ + { + "type": "service", + "id": "definition2" + }, + "%parameter%", + { + "class": "inline_service", + "public": true, + "synthetic": false, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "arguments": [ + "arg1", + "arg2" + ], + "file": null, + "tags": [] + }, + [ + "foo", + { + "type": "service", + "id": "definition2" + }, + { + "class": "inline_service", + "public": true, + "synthetic": false, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "arguments": [], + "file": null, + "tags": [] + } + ], + [ + { + "type": "service", + "id": "definition_1" + }, + { + "type": "service", + "id": "definition_2" + } + ], + [ + { + "type": "service", + "id": "definition1" + }, + "get" + ] + ], + "file": null, + "factory_class": "Full\\Qualified\\FactoryClass", + "factory_method": "get", + "tags": [] + } + }, + "aliases": { + "alias_1": { + "service": "service_1", + "public": true + }, + "alias_2": { + "service": "service_2", + "public": false + } + }, + "services": { + "service_container": "Symfony\\Component\\DependencyInjection\\ContainerBuilder" + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.md new file mode 100644 index 0000000000000..29e34d2b0d94f --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.md @@ -0,0 +1,39 @@ +Public services +=============== + +Definitions +----------- + +### definition_1 + +- Class: `Full\Qualified\Class1` +- Public: yes +- Synthetic: no +- Lazy: yes +- Shared: yes +- Abstract: yes +- Autowired: no +- Autoconfigured: no +- Arguments: yes +- Factory Class: `Full\Qualified\FactoryClass` +- Factory Method: `get` + + +Aliases +------- + +### alias_1 + +- Service: `service_1` +- Public: yes + +### alias_2 + +- Service: `service_2` +- Public: no + + +Services +-------- + +- `service_container`: `Symfony\Component\DependencyInjection\ContainerBuilder` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.txt b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.txt new file mode 100644 index 0000000000000..0e42596b99233 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.txt @@ -0,0 +1,13 @@ + +Symfony Container Public Services +================================= + + ------------------- -------------------------------------------------------- +  Service ID   Class name  + ------------------- -------------------------------------------------------- + alias_1 alias for "service_1" + alias_2 alias for "service_2" + definition_1 Full\Qualified\Class1 + service_container Symfony\Component\DependencyInjection\ContainerBuilder + ------------------- -------------------------------------------------------- + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.xml new file mode 100644 index 0000000000000..2d4de06aa84cb --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.xml @@ -0,0 +1,29 @@ + + + + + + + + %parameter% + + + arg1 + arg2 + + + + foo + + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.json index 170dfa2887e7c..7f86ccb2ac429 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.json @@ -7,14 +7,12 @@ "lazy": true, "shared": true, "abstract": true, + "autowire": false, + "autoconfigure": false, "file": null, "factory_class": "Full\\Qualified\\FactoryClass", "factory_method": "get", - "tags": [ - - ], - "autowire": false, - "autowiring_types": [] + "tags": [] } }, "aliases": { diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.md index 279958648f9ac..aff2f08191d00 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.md @@ -4,8 +4,7 @@ Public services Definitions ----------- -definition_1 -~~~~~~~~~~~~ +### definition_1 - Class: `Full\Qualified\Class1` - Public: yes @@ -14,6 +13,7 @@ definition_1 - Shared: yes - Abstract: yes - Autowired: no +- Autoconfigured: no - Factory Class: `Full\Qualified\FactoryClass` - Factory Method: `get` @@ -21,14 +21,12 @@ definition_1 Aliases ------- -alias_1 -~~~~~~~ +### alias_1 - Service: `service_1` - Public: yes -alias_2 -~~~~~~~ +### alias_2 - Service: `service_2` - Public: no diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.xml index 52031e59aa4ac..22a7fb4224127 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.xml @@ -2,7 +2,7 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.json index fe2b183901904..becd607b797a5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.json @@ -7,14 +7,12 @@ "lazy": true, "shared": true, "abstract": true, + "autowire": false, + "autoconfigure": false, "file": null, "factory_class": "Full\\Qualified\\FactoryClass", "factory_method": "get", - "tags": [ - - ], - "autowire": false, - "autowiring_types": [] + "tags": [] }, "definition_2": { "class": "Full\\Qualified\\Class2", @@ -23,9 +21,14 @@ "lazy": false, "shared": true, "abstract": false, + "autowire": false, + "autoconfigure": false, "file": "\/path\/to\/file", "factory_service": "factory.service", "factory_method": "get", + "calls": [ + "setMailer" + ], "tags": [ { "name": "tag1", @@ -42,16 +45,9 @@ }, { "name": "tag2", - "parameters": [ - - ] + "parameters": [] } - ], - "calls": [ - "setMailer" - ], - "autowire": false, - "autowiring_types": [] + ] } }, "aliases": { diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.md index f1e22ddba499c..54655668b435b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.md @@ -4,8 +4,7 @@ Public and private services Definitions ----------- -definition_1 -~~~~~~~~~~~~ +### definition_1 - Class: `Full\Qualified\Class1` - Public: yes @@ -14,11 +13,11 @@ definition_1 - Shared: yes - Abstract: yes - Autowired: no +- Autoconfigured: no - Factory Class: `Full\Qualified\FactoryClass` - Factory Method: `get` -definition_2 -~~~~~~~~~~~~ +### definition_2 - Class: `Full\Qualified\Class2` - Public: no @@ -27,6 +26,7 @@ definition_2 - Shared: yes - Abstract: no - Autowired: no +- Autoconfigured: no - File: `/path/to/file` - Factory Service: `factory.service` - Factory Method: `get` @@ -42,14 +42,12 @@ definition_2 Aliases ------- -alias_1 -~~~~~~~ +### alias_1 - Service: `service_1` - Public: yes -alias_2 -~~~~~~~ +### alias_2 - Service: `service_2` - Public: no diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.xml index bde934fa50a39..54da4f4f48427 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.xml @@ -2,10 +2,10 @@ - + - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.json index a25a34b44a89d..fb9634f67a309 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.json @@ -7,9 +7,14 @@ "lazy": false, "shared": true, "abstract": false, + "autowire": false, + "autoconfigure": false, "file": "\/path\/to\/file", "factory_service": "factory.service", "factory_method": "get", + "calls": [ + "setMailer" + ], "tags": [ { "name": "tag1", @@ -26,22 +31,11 @@ }, { "name": "tag2", - "parameters": [ - - ] + "parameters": [] } - ], - "calls": [ - "setMailer" - ], - "autowire": false, - "autowiring_types": [] + ] } }, - "aliases": [ - - ], - "services": [ - - ] + "aliases": [], + "services": [] } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.md index 8a9636f71db16..9895f1fb5d8b2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.md @@ -4,8 +4,7 @@ Public and private services with tag `tag1` Definitions ----------- -definition_2 -~~~~~~~~~~~~ +### definition_2 - Class: `Full\Qualified\Class2` - Public: no @@ -14,6 +13,7 @@ definition_2 - Shared: yes - Abstract: no - Autowired: no +- Autoconfigured: no - File: `/path/to/file` - Factory Service: `factory.service` - Factory Method: `get` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.xml index d679db7b3b860..9c39b653dd5fc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.xml @@ -1,6 +1,6 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.json index 25138d78fd42c..7f49ac241ffde 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.json @@ -7,14 +7,14 @@ "lazy": false, "shared": true, "abstract": false, + "autowire": false, + "autoconfigure": false, "file": "\/path\/to\/file", - "calls": [ - "setMailer" - ], "factory_service": "factory.service", "factory_method": "get", - "autowire": false, - "autowiring_types": [] + "calls": [ + "setMailer" + ] } ], "tag2": [ @@ -25,14 +25,14 @@ "lazy": false, "shared": true, "abstract": false, + "autowire": false, + "autoconfigure": false, "file": "\/path\/to\/file", - "calls": [ - "setMailer" - ], "factory_service": "factory.service", "factory_method": "get", - "autowire": false, - "autowiring_types": [] + "calls": [ + "setMailer" + ] } ] } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.md index 1731fc9f917ea..205596259d2c6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.md @@ -4,8 +4,7 @@ Container tags tag1 ---- -definition_2 -~~~~~~~~~~~~ +### definition_2 - Class: `Full\Qualified\Class2` - Public: no @@ -14,6 +13,7 @@ definition_2 - Shared: yes - Abstract: no - Autowired: no +- Autoconfigured: no - File: `/path/to/file` - Factory Service: `factory.service` - Factory Method: `get` @@ -23,8 +23,7 @@ definition_2 tag2 ---- -definition_2 -~~~~~~~~~~~~ +### definition_2 - Class: `Full\Qualified\Class2` - Public: no @@ -33,6 +32,7 @@ definition_2 - Shared: yes - Abstract: no - Autowired: no +- Autoconfigured: no - File: `/path/to/file` - Factory Service: `factory.service` - Factory Method: `get` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.xml index 08352fa206877..4e98e77a19a23 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.xml @@ -1,7 +1,7 @@ - + @@ -9,7 +9,7 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.json index d11d6e92d49fc..940bab12699b1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.json @@ -5,12 +5,10 @@ "lazy": true, "shared": true, "abstract": true, + "autowire": false, + "autoconfigure": false, "file": null, "factory_class": "Full\\Qualified\\FactoryClass", "factory_method": "get", - "tags": [ - - ], - "autowire": false, - "autowiring_types": [] + "tags": [] } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.md index b1d46b69f4f9f..e4d0429b9087d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.md @@ -5,5 +5,6 @@ - Shared: yes - Abstract: yes - Autowired: no +- Autoconfigured: no - Factory Class: `Full\Qualified\FactoryClass` - Factory Method: `get` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.txt b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.txt index f74cdbbccd5cc..596d918579071 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.txt +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.txt @@ -1,17 +1,16 @@ - ------------------ ----------------------------- -  Option   Value  - ------------------ ----------------------------- - Service ID - - Class Full\Qualified\Class1 - Tags - - Public yes - Synthetic no - Lazy yes - Shared yes - Abstract yes - Autowired no - Autowiring Types - - Factory Class Full\Qualified\FactoryClass - Factory Method get - ------------------ ----------------------------- + ---------------- ----------------------------- +  Option   Value  + ---------------- ----------------------------- + Service ID - + Class Full\Qualified\Class1 + Tags - + Public yes + Synthetic no + Lazy yes + Shared yes + Abstract yes + Autowired no + Factory Class Full\Qualified\FactoryClass + Factory Method get + ---------------- ----------------------------- diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.xml index 67d102945a6fe..d5015fdde3bd7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.xml @@ -1,4 +1,4 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.json index 8a6611a37cd7d..1c7b1ae14af42 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.json @@ -5,9 +5,14 @@ "lazy": false, "shared": true, "abstract": false, + "autowire": false, + "autoconfigure": false, "file": "\/path\/to\/file", "factory_service": "factory.service", "factory_method": "get", + "calls": [ + "setMailer" + ], "tags": [ { "name": "tag1", @@ -24,14 +29,7 @@ }, { "name": "tag2", - "parameters": [ - - ] + "parameters": [] } - ], - "calls": [ - "setMailer" - ], - "autowire": false, - "autowiring_types": [] + ] } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.md index 1f097a2585321..6ff6d60b3dc93 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.md @@ -5,6 +5,7 @@ - Shared: yes - Abstract: no - Autowired: no +- Autoconfigured: no - File: `/path/to/file` - Factory Service: `factory.service` - Factory Method: `get` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.txt b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.txt index 86f44b3590946..a8ed854751b22 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.txt +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.txt @@ -1,19 +1,20 @@ - ------------------ ------------------------------------------------------- -  Option   Value  - ------------------ ------------------------------------------------------- - Service ID - - Class Full\Qualified\Class2 - Tags tag1 (attr1: val1, attr2: val2)tag1 (attr3: val3)tag2 - Calls setMailer - Public no - Synthetic yes - Lazy no - Shared yes - Abstract no - Autowired no - Autowiring Types - - Required File /path/to/file - Factory Service factory.service - Factory Method get - ------------------ ------------------------------------------------------- + ----------------- --------------------------------- +  Option   Value  + ----------------- --------------------------------- + Service ID - + Class Full\Qualified\Class2 + Tags tag1 (attr1: val1, attr2: val2)  + tag1 (attr3: val3)  + tag2 + Calls setMailer + Public no + Synthetic yes + Lazy no + Shared yes + Abstract no + Autowired no + Required File /path/to/file + Factory Service factory.service + Factory Method get + ----------------- --------------------------------- diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.xml index 434a34b2565c4..72319eca97a4c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.xml @@ -1,5 +1,5 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.json new file mode 100644 index 0000000000000..0590d3f76a611 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.json @@ -0,0 +1,74 @@ +{ + "class": "Full\\Qualified\\Class1", + "public": true, + "synthetic": false, + "lazy": true, + "shared": true, + "abstract": true, + "autowire": false, + "autoconfigure": false, + "arguments": [ + { + "type": "service", + "id": "definition2" + }, + "%parameter%", + { + "class": "inline_service", + "public": true, + "synthetic": false, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "arguments": [ + "arg1", + "arg2" + ], + "file": null, + "tags": [] + }, + [ + "foo", + { + "type": "service", + "id": "definition2" + }, + { + "class": "inline_service", + "public": true, + "synthetic": false, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "arguments": [], + "file": null, + "tags": [] + } + ], + [ + { + "type": "service", + "id": "definition_1" + }, + { + "type": "service", + "id": "definition_2" + } + ], + [ + { + "type": "service", + "id": "definition1" + }, + "get" + ] + ], + "file": null, + "factory_class": "Full\\Qualified\\FactoryClass", + "factory_method": "get", + "tags": [] +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.md new file mode 100644 index 0000000000000..7cee17ad9cbb4 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.md @@ -0,0 +1,11 @@ +- Class: `Full\Qualified\Class1` +- Public: yes +- Synthetic: no +- Lazy: yes +- Shared: yes +- Abstract: yes +- Autowired: no +- Autoconfigured: no +- Arguments: yes +- Factory Class: `Full\Qualified\FactoryClass` +- Factory Method: `get` \ No newline at end of file diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.txt b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.txt new file mode 100644 index 0000000000000..c372da1d87298 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.txt @@ -0,0 +1,22 @@ + ---------------- ------------------------------------------- +  Option   Value  + ---------------- ------------------------------------------- + Service ID - + Class Full\Qualified\Class1 + Tags - + Public yes + Synthetic no + Lazy yes + Shared yes + Abstract yes + Autowired no + Factory Class Full\Qualified\FactoryClass + Factory Method get + Arguments Service(definition2)  + %parameter%  + Inlined Service  + Array (3 element(s))  + Iterator (2 element(s))  + ClosureProxy(Service(definition1)::get()) + ---------------- ------------------------------------------- + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.xml new file mode 100644 index 0000000000000..6d3cb8eea40be --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.xml @@ -0,0 +1,24 @@ + + + + + %parameter% + + + arg1 + arg2 + + + + foo + + + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_2.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_2.json new file mode 100644 index 0000000000000..8f65d27b83ea9 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_2.json @@ -0,0 +1,36 @@ +{ + "class": "Full\\Qualified\\Class2", + "public": false, + "synthetic": true, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "arguments": [], + "file": "\/path\/to\/file", + "factory_service": "factory.service", + "factory_method": "get", + "calls": [ + "setMailer" + ], + "tags": [ + { + "name": "tag1", + "parameters": { + "attr1": "val1", + "attr2": "val2" + } + }, + { + "name": "tag1", + "parameters": { + "attr3": "val3" + } + }, + { + "name": "tag2", + "parameters": [] + } + ] +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_2.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_2.md new file mode 100644 index 0000000000000..abe7dd475db2c --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_2.md @@ -0,0 +1,19 @@ +- Class: `Full\Qualified\Class2` +- Public: no +- Synthetic: yes +- Lazy: no +- Shared: yes +- Abstract: no +- Autowired: no +- Autoconfigured: no +- Arguments: no +- File: `/path/to/file` +- Factory Service: `factory.service` +- Factory Method: `get` +- Call: `setMailer` +- Tag: `tag1` + - Attr1: val1 + - Attr2: val2 +- Tag: `tag1` + - Attr3: val3 +- Tag: `tag2` \ No newline at end of file diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_2.txt b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_2.txt new file mode 100644 index 0000000000000..a8ed854751b22 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_2.txt @@ -0,0 +1,20 @@ + ----------------- --------------------------------- +  Option   Value  + ----------------- --------------------------------- + Service ID - + Class Full\Qualified\Class2 + Tags tag1 (attr1: val1, attr2: val2)  + tag1 (attr3: val3)  + tag2 + Calls setMailer + Public no + Synthetic yes + Lazy no + Shared yes + Abstract no + Autowired no + Required File /path/to/file + Factory Service factory.service + Factory Method get + ----------------- --------------------------------- + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_2.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_2.xml new file mode 100644 index 0000000000000..72319eca97a4c --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_2.xml @@ -0,0 +1,17 @@ + + + + + + + + + val1 + val2 + + + val3 + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_1.txt b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_1.txt index 587543fb0a47f..ed0bcca6562eb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_1.txt +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_1.txt @@ -11,7 +11,7 @@ | Requirements | name: [a-z]+ | | Class | Symfony\Component\Routing\Route | | Defaults | name: Joseph | -| Options | compiler_class: Symfony\Component\Routing\RouteCompiler | -| | opt1: val1 | -| | opt2: val2 | +| Options | compiler_class: Symfony\Component\Routing\RouteCompiler | +| | opt1: val1 | +| | opt2: val2 | +--------------+---------------------------------------------------------+ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_2.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_2.json index d34b3a77aec6e..58caf26d537e7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_2.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_2.json @@ -6,9 +6,7 @@ "scheme": "http|https", "method": "PUT|POST", "class": "Symfony\\Component\\Routing\\Route", - "defaults": [ - - ], + "defaults": [], "requirements": "NO CUSTOM", "options": { "compiler_class": "Symfony\\Component\\Routing\\RouteCompiler", diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_2.txt b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_2.txt index e75c19e13112c..828f6316bedb9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_2.txt +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_2.txt @@ -11,7 +11,7 @@ | Requirements | NO CUSTOM | | Class | Symfony\Component\Routing\Route | | Defaults | NONE | -| Options | compiler_class: Symfony\Component\Routing\RouteCompiler | -| | opt1: val1 | -| | opt2: val2 | +| Options | compiler_class: Symfony\Component\Routing\RouteCompiler | +| | opt1: val1 | +| | opt2: val2 | +--------------+---------------------------------------------------------+ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_collection_1.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_collection_1.json index 7170953f5fee7..350bffdb3a9c7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_collection_1.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_collection_1.json @@ -27,9 +27,7 @@ "scheme": "http|https", "method": "PUT|POST", "class": "Symfony\\Component\\Routing\\Route", - "defaults": [ - - ], + "defaults": [], "requirements": "NO CUSTOM", "options": { "compiler_class": "Symfony\\Component\\Routing\\RouteCompiler", diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/views/translation.html.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/views/translation.html.php index c0ae6ec5c6604..5a7cd354763d0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/views/translation.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/views/translation.html.php @@ -31,3 +31,19 @@ 10, array('%count%' => 10) ) ?> + +trans('other-domain-test-no-params-short-array', [], 'not_messages'); ?> + +trans('other-domain-test-no-params-long-array', array(), 'not_messages'); ?> + +trans('other-domain-test-params-short-array', ['foo' => 'bar'], 'not_messages'); ?> + +trans('other-domain-test-params-long-array', array('foo' => 'bar'), 'not_messages'); ?> + +transChoice('other-domain-test-trans-choice-short-array-%count%', 10, ['%count%' => 10], 'not_messages'); ?> + +transChoice('other-domain-test-trans-choice-long-array-%count%', 10, array('%count%' => 10), 'not_messages'); ?> + +trans('typecast', ['a' => (int) '123'], 'not_messages'); ?> +transChoice('msg1', 10 + 1, [], 'not_messages'); ?> +transChoice('msg2', ceil(4.5), [], 'not_messages'); ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Validation/Article.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Validation/Article.php new file mode 100644 index 0000000000000..a1ecee6d0b375 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Validation/Article.php @@ -0,0 +1,8 @@ + - - + + + + @@ -27,7 +29,7 @@ - + %security.authentication.manager.erase_credentials% @@ -48,12 +50,14 @@ + - + + @@ -67,6 +71,7 @@ + %security.role_hierarchy.roles% @@ -97,15 +102,17 @@ - + + + - - + + diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_acl_dbal.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_acl_dbal.xml index 581c938964e67..b8660b4bc554a 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_acl_dbal.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_acl_dbal.xml @@ -35,6 +35,7 @@ + diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_debug.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_debug.xml index eb22cbddf504e..e03f578784a33 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_debug.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_debug.xml @@ -5,7 +5,7 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - + diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml index 7bdaf628cf40c..45ebde81f1325 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml @@ -140,7 +140,21 @@ - + + + + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig b/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig index 3830e924a190b..073d0d869d8f0 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig +++ b/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig @@ -257,7 +257,13 @@ : 'DENIED' }} - {{ decision.attributes|length == 1 ? decision.attributes|first : profiler_dump(decision.attributes) }} + + {% if decision.attributes|length == 1 %} + {{ decision.attributes|first }} + {% else %} + {{ profiler_dump(decision.attributes) }} + {% endif %} + {{ profiler_dump(decision.object) }} {% endfor %} diff --git a/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php b/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php index 81b815e4d973e..02f2739ed8a2f 100644 --- a/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php +++ b/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php @@ -37,8 +37,23 @@ public function getConfig() return $this->config; } + /** + * @deprecated since version 3.3, will be removed in 4.0. Use {@link getListeners()} and/or {@link getExceptionListener()} instead. + */ public function getContext() { - return array($this->listeners, $this->exceptionListener); + @trigger_error(sprintf('Method %s() is deprecated since version 3.3 and will be removed in 4.0. Use %s::getListeners/getExceptionListener() instead.', __METHOD__, __CLASS__), E_USER_DEPRECATED); + + return array($this->getListeners(), $this->getExceptionListener()); + } + + public function getListeners() + { + return $this->listeners; + } + + public function getExceptionListener() + { + return $this->exceptionListener; } } diff --git a/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php b/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php index f833a63e65966..55272ec3043bd 100644 --- a/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php +++ b/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php @@ -11,9 +11,9 @@ namespace Symfony\Bundle\SecurityBundle\Security; +use Psr\Container\ContainerInterface; use Symfony\Component\Security\Http\FirewallMapInterface; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\DependencyInjection\ContainerInterface; /** * This is a lazy-loading firewall map implementation. @@ -22,22 +22,100 @@ * * @author Johannes M. Schmitt */ -class FirewallMap implements FirewallMapInterface +class FirewallMap extends _FirewallMap implements FirewallMapInterface { - protected $container; - protected $map; - private $contexts; + /** + * @deprecated since version 3.3, to be removed in 4.0 alongside with magic methods below + */ + private $container; - public function __construct(ContainerInterface $container, array $map) + /** + * @deprecated since version 3.3, to be removed in 4.0 alongside with magic methods below + */ + private $map; + + public function __construct(ContainerInterface $container, $map) { + parent::__construct($container, $map); $this->container = $container; $this->map = $map; - $this->contexts = new \SplObjectStorage(); } /** - * {@inheritdoc} + * @internal */ + public function __get($name) + { + if ('map' === $name || 'container' === $name) { + @trigger_error(sprintf('Using the "%s::$%s" property is deprecated since version 3.3 as it will be removed/private in 4.0.', __CLASS__, $name), E_USER_DEPRECATED); + + if ('map' === $name && $this->map instanceof \Traversable) { + $this->map = iterator_to_array($this->map); + } + } + + return $this->$name; + } + + /** + * @internal + */ + public function __set($name, $value) + { + if ('map' === $name || 'container' === $name) { + @trigger_error(sprintf('Using the "%s::$%s" property is deprecated since version 3.3 as it will be removed/private in 4.0.', __CLASS__, $name), E_USER_DEPRECATED); + + $set = \Closure::bind(function ($name, $value) { $this->$name = $value; }, $this, parent::class); + $set($name, $value); + } + + $this->$name = $value; + } + + /** + * @internal + */ + public function __isset($name) + { + if ('map' === $name || 'container' === $name) { + @trigger_error(sprintf('Using the "%s::$%s" property is deprecated since version 3.3 as it will be removed/private in 4.0.', __CLASS__, $name), E_USER_DEPRECATED); + } + + return isset($this->$name); + } + + /** + * @internal + */ + public function __unset($name) + { + if ('map' === $name || 'container' === $name) { + @trigger_error(sprintf('Using the "%s::$%s" property is deprecated since version 3.3 as it will be removed/private in 4.0.', __CLASS__, $name), E_USER_DEPRECATED); + + $unset = \Closure::bind(function ($name) { unset($this->$name); }, $this, parent::class); + $unset($name); + } + + unset($this->$name); + } +} + +/** + * @internal to be removed in 4.0 + */ +class _FirewallMap +{ + private $container; + private $map; + private $contexts; + + public function __construct(ContainerInterface $container, $map) + { + $this->container = $container; + $this->map = $map; + $this->contexts = new \SplObjectStorage(); + } + public function getListeners(Request $request) { $context = $this->getFirewallContext($request); @@ -46,7 +124,7 @@ public function getListeners(Request $request) return array(array(), null); } - return $context->getContext(); + return array($context->getListeners(), $context->getExceptionListener()); } /** diff --git a/src/Symfony/Bundle/SecurityBundle/SecurityBundle.php b/src/Symfony/Bundle/SecurityBundle/SecurityBundle.php index f2dfc991fbcef..c50aab24e12e4 100644 --- a/src/Symfony/Bundle/SecurityBundle/SecurityBundle.php +++ b/src/Symfony/Bundle/SecurityBundle/SecurityBundle.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\SecurityBundle; +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\JsonLoginFactory; use Symfony\Component\HttpKernel\Bundle\Bundle; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\AddSecurityVotersPass; @@ -42,6 +43,7 @@ public function build(ContainerBuilder $container) $extension = $container->getExtension('security'); $extension->addSecurityListenerFactory(new FormLoginFactory()); $extension->addSecurityListenerFactory(new FormLoginLdapFactory()); + $extension->addSecurityListenerFactory(new JsonLoginFactory()); $extension->addSecurityListenerFactory(new HttpBasicFactory()); $extension->addSecurityListenerFactory(new HttpBasicLdapFactory()); $extension->addSecurityListenerFactory(new HttpDigestFactory()); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php index 2d912721ed4f4..f155f901c9c3c 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\SecurityBundle\Tests\DataCollector; +use PHPUnit\Framework\TestCase; use Symfony\Bundle\SecurityBundle\DataCollector\SecurityDataCollector; use Symfony\Bundle\SecurityBundle\Security\FirewallConfig; use Symfony\Bundle\SecurityBundle\Security\FirewallMap; @@ -20,7 +21,7 @@ use Symfony\Component\Security\Core\Role\RoleHierarchy; use Symfony\Component\Security\Http\FirewallMapInterface; -class SecurityDataCollectorTest extends \PHPUnit_Framework_TestCase +class SecurityDataCollectorTest extends TestCase { public function testCollectWhenSecurityIsDisabled() { @@ -62,17 +63,14 @@ public function testCollectAuthenticationTokenAndRoles(array $roles, array $norm $collector = new SecurityDataCollector($tokenStorage, $this->getRoleHierarchy()); $collector->collect($this->getRequest(), $this->getResponse()); + $collector->lateCollect(); $this->assertTrue($collector->isEnabled()); $this->assertTrue($collector->isAuthenticated()); - $this->assertSame('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $collector->getTokenClass()); + $this->assertSame('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $collector->getTokenClass()->getValue()); $this->assertTrue($collector->supportsRoleHierarchy()); - $this->assertSame($normalizedRoles, $collector->getRoles()->getRawData()[1]); - if ($inheritedRoles) { - $this->assertSame($inheritedRoles, $collector->getInheritedRoles()->getRawData()[1]); - } else { - $this->assertSame($inheritedRoles, $collector->getInheritedRoles()->getRawData()[0][0]); - } + $this->assertSame($normalizedRoles, $collector->getRoles()->getValue(true)); + $this->assertSame($inheritedRoles, $collector->getInheritedRoles()->getValue(true)); $this->assertSame('hhamon', $collector->getUser()); } @@ -93,6 +91,7 @@ public function testGetFirewall() $collector = new SecurityDataCollector(null, null, null, null, $firewallMap); $collector->collect($request, $this->getResponse()); + $collector->lateCollect(); $collected = $collector->getFirewall(); $this->assertSame($firewallConfig->getName(), $collected['name']); @@ -106,7 +105,7 @@ public function testGetFirewall() $this->assertSame($firewallConfig->getAccessDeniedHandler(), $collected['access_denied_handler']); $this->assertSame($firewallConfig->getAccessDeniedUrl(), $collected['access_denied_url']); $this->assertSame($firewallConfig->getUserChecker(), $collected['user_checker']); - $this->assertSame($firewallConfig->getListeners(), $collected['listeners']->getRawData()[0][0]); + $this->assertSame($firewallConfig->getListeners(), $collected['listeners']->getValue()); } public function testGetFirewallReturnsNull() diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/AddSecurityVotersPassTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/AddSecurityVotersPassTest.php new file mode 100644 index 0000000000000..66d6bde205975 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/AddSecurityVotersPassTest.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\AddSecurityVotersPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +class AddSecurityVotersPassTest extends TestCase +{ + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException + */ + public function testNoVoters() + { + $container = new ContainerBuilder(); + $container + ->register('security.access.decision_manager', 'Symfony\Component\Security\Core\Authorization\AccessDecisionManager') + ->addArgument(array()) + ; + + $compilerPass = new AddSecurityVotersPass(); + $compilerPass->process($container); + } + + public function testThatSecurityVotersAreProcessedInPriorityOrder() + { + $container = new ContainerBuilder(); + $container + ->register('security.access.decision_manager', 'Symfony\Component\Security\Core\Authorization\AccessDecisionManager') + ->addArgument(array()) + ; + $container + ->register('no_prio_service') + ->addTag('security.voter') + ; + $container + ->register('lowest_prio_service') + ->addTag('security.voter', array('priority' => 100)) + ; + $container + ->register('highest_prio_service') + ->addTag('security.voter', array('priority' => 200)) + ; + $container + ->register('zero_prio_service') + ->addTag('security.voter', array('priority' => 0)) + ; + $compilerPass = new AddSecurityVotersPass(); + $compilerPass->process($container); + + $argument = $container->getDefinition('security.access.decision_manager')->getArgument(0); + $refs = $argument->getValues(); + $this->assertEquals(new Reference('highest_prio_service'), $refs[0]); + $this->assertEquals(new Reference('lowest_prio_service'), $refs[1]); + $this->assertCount(4, $refs); + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php index bbce61a75813b..bfa0f1b877b28 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php @@ -11,12 +11,13 @@ namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection; +use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Reference; use Symfony\Bundle\SecurityBundle\SecurityBundle; use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension; use Symfony\Component\DependencyInjection\ContainerBuilder; -abstract class CompleteConfigurationTest extends \PHPUnit_Framework_TestCase +abstract class CompleteConfigurationTest extends TestCase { private static $containerCache = array(); @@ -68,12 +69,12 @@ public function testFirewalls() $arguments = $container->getDefinition('security.firewall.map')->getArguments(); $listeners = array(); $configs = array(); - foreach (array_keys($arguments[1]) as $contextId) { + foreach (array_keys($arguments[1]->getValues()) as $contextId) { $contextDef = $container->getDefinition($contextId); $arguments = $contextDef->getArguments(); - $listeners[] = array_map(function ($ref) { return (string) $ref; }, $arguments['index_0']); + $listeners[] = array_map('strval', $arguments['index_0']); - $configDef = $container->getDefinition($arguments['index_2']); + $configDef = $container->getDefinition((string) $arguments['index_2']); $configs[] = array_values($configDef->getArguments()); } @@ -171,6 +172,8 @@ public function testFirewalls() 'security.access_listener', ), ), $listeners); + + $this->assertFalse($container->hasAlias('Symfony\Component\Security\Core\User\UserCheckerInterface', 'No user checker alias is registered when custom user checker services are registered')); } public function testFirewallRequestMatchers() @@ -180,7 +183,7 @@ public function testFirewallRequestMatchers() $arguments = $container->getDefinition('security.firewall.map')->getArguments(); $matchers = array(); - foreach ($arguments[1] as $reference) { + foreach ($arguments[1]->getValues() as $reference) { if ($reference instanceof Reference) { $definition = $container->getDefinition((string) $reference); $matchers[] = $definition->getArguments(); @@ -199,6 +202,14 @@ public function testFirewallRequestMatchers() ), $matchers); } + public function testUserCheckerAliasIsRegistered() + { + $container = $this->getContainer('no_custom_user_checker'); + + $this->assertTrue($container->hasAlias('Symfony\Component\Security\Core\User\UserCheckerInterface', 'Alias for user checker is registered when no custom user checker service is registered')); + $this->assertFalse($container->getAlias('Symfony\Component\Security\Core\User\UserCheckerInterface')->isPublic()); + } + public function testAccess() { $container = $this->getContainer('container1'); @@ -234,7 +245,7 @@ public function testAccess() ); } elseif (3 === $i) { $this->assertEquals('IS_AUTHENTICATED_ANONYMOUSLY', $attributes[0]); - $expression = $container->getDefinition($attributes[1])->getArgument(0); + $expression = $container->getDefinition((string) $attributes[1])->getArgument(0); $this->assertEquals("token.getUsername() matches '/^admin/'", $expression); } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/no_custom_user_checker.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/no_custom_user_checker.php new file mode 100644 index 0000000000000..b08d9c60c82f8 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/no_custom_user_checker.php @@ -0,0 +1,28 @@ +loadFromExtension('security', array( + 'providers' => array( + 'default' => array( + 'memory' => array( + 'users' => array( + 'foo' => array('password' => 'foo', 'roles' => 'ROLE_USER'), + ), + ), + ), + ), + 'firewalls' => array( + 'simple' => array('pattern' => '/login', 'security' => false), + 'secure' => array('stateless' => true, + 'http_basic' => true, + 'http_digest' => array('secret' => 'TheSecret'), + 'form_login' => true, + 'anonymous' => true, + 'switch_user' => true, + 'x509' => true, + 'remote_user' => true, + 'logout' => true, + 'remember_me' => array('secret' => 'TheSecret'), + 'user_checker' => null, + ), + ), +)); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/no_custom_user_checker.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/no_custom_user_checker.xml new file mode 100644 index 0000000000000..7d648ae1baec2 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/no_custom_user_checker.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/no_custom_user_checker.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/no_custom_user_checker.yml new file mode 100644 index 0000000000000..23afa8cd9b3c5 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/no_custom_user_checker.yml @@ -0,0 +1,23 @@ +security: + providers: + default: + memory: + users: + foo: { password: foo, roles: ROLE_USER } + + firewalls: + simple: { pattern: /login, security: false } + secure: + stateless: true + http_basic: true + http_digest: + secret: TheSecret + form_login: true + anonymous: true + switch_user: true + x509: true + remote_user: true + logout: true + remember_me: + secret: TheSecret + user_checker: ~ diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php index 8a8f5c8329dea..a64c4fe4101f1 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php @@ -11,10 +11,11 @@ namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection; +use PHPUnit\Framework\TestCase; use Symfony\Bundle\SecurityBundle\DependencyInjection\MainConfiguration; use Symfony\Component\Config\Definition\Processor; -class MainConfigurationTest extends \PHPUnit_Framework_TestCase +class MainConfigurationTest extends TestCase { /** * The minimal, required config needed to not have any required validation diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php index 39daf998cb4e7..846d76f770d59 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php @@ -11,10 +11,11 @@ namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Security\Factory; +use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ContainerBuilder; -class AbstractFactoryTest extends \PHPUnit_Framework_TestCase +class AbstractFactoryTest extends TestCase { public function testCreate() { diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/GuardAuthenticationFactoryTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/GuardAuthenticationFactoryTest.php index 4c1634850275c..55a3ad387c224 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/GuardAuthenticationFactoryTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/GuardAuthenticationFactoryTest.php @@ -11,12 +11,14 @@ namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Security\Factory; +use PHPUnit\Framework\TestCase; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\GuardAuthenticationFactory; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; -class GuardAuthenticationFactoryTest extends \PHPUnit_Framework_TestCase +class GuardAuthenticationFactoryTest extends TestCase { /** * @dataProvider getValidConfigurationTests @@ -106,7 +108,7 @@ public function testBasicCreate() $providerDefinition = $container->getDefinition('security.authentication.provider.guard.my_firewall'); $this->assertEquals(array( - 'index_0' => array(new Reference('authenticator123')), + 'index_0' => new IteratorArgument(array(new Reference('authenticator123'))), 'index_1' => new Reference('my_user_provider'), 'index_2' => 'my_firewall', 'index_3' => new Reference('security.user_checker.my_firewall'), @@ -114,7 +116,7 @@ public function testBasicCreate() $listenerDefinition = $container->getDefinition('security.authentication.listener.guard.my_firewall'); $this->assertEquals('my_firewall', $listenerDefinition->getArgument(2)); - $this->assertEquals(array(new Reference('authenticator123')), $listenerDefinition->getArgument(3)); + $this->assertEquals(array(new Reference('authenticator123')), $listenerDefinition->getArgument(3)->getValues()); } public function testExistingDefaultEntryPointUsed() diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php index dce30c758b976..72ef2e0c3ed56 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php @@ -11,12 +11,13 @@ namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection; +use PHPUnit\Framework\TestCase; use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension; use Symfony\Bundle\SecurityBundle\SecurityBundle; use Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Fixtures\UserProvider\DummyProvider; use Symfony\Component\DependencyInjection\ContainerBuilder; -class SecurityExtensionTest extends \PHPUnit_Framework_TestCase +class SecurityExtensionTest extends TestCase { /** * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AutowiringTypesTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AutowiringTypesTest.php new file mode 100644 index 0000000000000..a0bee3c01c1ca --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AutowiringTypesTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional; + +use Symfony\Component\Security\Core\Authorization\AccessDecisionManager; +use Symfony\Component\Security\Core\Authorization\TraceableAccessDecisionManager; + +class AutowiringTypesTest extends WebTestCase +{ + public function testAccessDecisionManagerAutowiring() + { + static::bootKernel(array('debug' => false)); + $container = static::$kernel->getContainer(); + + $autowiredServices = $container->get('test.autowiring_types.autowired_services'); + $this->assertInstanceOf(AccessDecisionManager::class, $autowiredServices->getAccessDecisionManager(), 'The security.access.decision_manager service should be injected in debug mode'); + + static::bootKernel(array('debug' => true)); + $container = static::$kernel->getContainer(); + + $autowiredServices = $container->get('test.autowiring_types.autowired_services'); + $this->assertInstanceOf(TraceableAccessDecisionManager::class, $autowiredServices->getAccessDecisionManager(), 'The debug.security.access.decision_manager service should be injected in non-debug mode'); + } + + protected static function createKernel(array $options = array()) + { + return parent::createKernel(array('test_case' => 'AutowiringTypes') + $options); + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AutowiringBundle/AutowiredServices.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AutowiringBundle/AutowiredServices.php new file mode 100644 index 0000000000000..30aa62f1f66ec --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AutowiringBundle/AutowiredServices.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AutowiringBundle; + +use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; + +class AutowiredServices +{ + private $accessDecisionManager; + + public function __construct(AccessDecisionManagerInterface $accessDecisionManager) + { + $this->accessDecisionManager = $accessDecisionManager; + } + + public function getAccessDecisionManager() + { + return $this->accessDecisionManager; + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AutowiringBundle/AutowiringBundle.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AutowiringBundle/AutowiringBundle.php new file mode 100644 index 0000000000000..d57031a018fe9 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AutowiringBundle/AutowiringBundle.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\Bundle\SecurityBundle\Tests\Functional\Bundle\AutowiringBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class AutowiringBundle extends Bundle +{ +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Controller/LoginController.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Controller/LoginController.php index c232b4cfd9029..90f4d355a4871 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Controller/LoginController.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Controller/LoginController.php @@ -24,14 +24,14 @@ public function loginAction() { $form = $this->container->get('form.factory')->create('Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\CsrfFormLoginBundle\Form\UserLoginType'); - return $this->container->get('templating')->renderResponse('CsrfFormLoginBundle:Login:login.html.twig', array( + return new Response($this->container->get('twig')->render('@CsrfFormLogin/Login/login.html.twig', array( 'form' => $form->createView(), - )); + ))); } public function afterLoginAction() { - return $this->container->get('templating')->renderResponse('CsrfFormLoginBundle:Login:after_login.html.twig'); + return new Response($this->container->get('twig')->render('@CsrfFormLogin/Login/after_login.html.twig')); } public function loginCheckAction() diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Resources/views/Login/after_login.html.twig b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Resources/views/Login/after_login.html.twig index b56c186ecbb60..a117cb94f8778 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Resources/views/Login/after_login.html.twig +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Resources/views/Login/after_login.html.twig @@ -1,4 +1,4 @@ -{% extends "::base.html.twig" %} +{% extends "base.html.twig" %} {% block body %} Hello {{ app.user.username }}!

diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Resources/views/Login/login.html.twig b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Resources/views/Login/login.html.twig index 36ae0151d0d00..a21ea7259b1b5 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Resources/views/Login/login.html.twig +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Resources/views/Login/login.html.twig @@ -1,4 +1,4 @@ -{% extends "::base.html.twig" %} +{% extends "base.html.twig" %} {% block body %} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LocalizedController.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LocalizedController.php index 7a6faf4cc99d2..f8b4d9068f7b1 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LocalizedController.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LocalizedController.php @@ -30,11 +30,11 @@ public function loginAction(Request $request) $error = $request->getSession()->get(Security::AUTHENTICATION_ERROR); } - return $this->container->get('templating')->renderResponse('FormLoginBundle:Localized:login.html.twig', array( + return new Response($this->container->get('twig')->render('@FormLogin/Localized/login.html.twig', array( // last username entered by the user 'last_username' => $request->getSession()->get(Security::LAST_USERNAME), 'error' => $error, - )); + ))); } public function loginCheckAction() diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LoginController.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LoginController.php index acc8f43255494..658a3cd57c24f 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LoginController.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LoginController.php @@ -32,16 +32,16 @@ public function loginAction(Request $request, UserInterface $user = null) $error = $request->getSession()->get(Security::AUTHENTICATION_ERROR); } - return $this->container->get('templating')->renderResponse('FormLoginBundle:Login:login.html.twig', array( + return new Response($this->container->get('twig')->render('@FormLogin/Login/login.html.twig', array( // last username entered by the user 'last_username' => $request->getSession()->get(Security::LAST_USERNAME), 'error' => $error, - )); + ))); } public function afterLoginAction(UserInterface $user) { - return $this->container->get('templating')->renderResponse('FormLoginBundle:Login:after_login.html.twig', array('user' => $user)); + return new Response($this->container->get('twig')->render('@FormLogin/Login/after_login.html.twig', array('user' => $user))); } public function loginCheckAction() diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Localized/login.html.twig b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Localized/login.html.twig index 60dd2f1ffac7b..de0da3bb589c0 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Localized/login.html.twig +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Localized/login.html.twig @@ -1,4 +1,4 @@ -{% extends "::base.html.twig" %} +{% extends "base.html.twig" %} {% block body %} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Login/after_login.html.twig b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Login/after_login.html.twig index 8f6a76098597f..3f88aae903536 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Login/after_login.html.twig +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Login/after_login.html.twig @@ -1,4 +1,4 @@ -{% extends "::base.html.twig" %} +{% extends "base.html.twig" %} {% block body %} Hello {{ user.username }}!

diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Login/login.html.twig b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Login/login.html.twig index 6c1a22471e10c..059f5f2bca1d2 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Login/login.html.twig +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Login/login.html.twig @@ -1,4 +1,4 @@ -{% extends "::base.html.twig" %} +{% extends "base.html.twig" %} {% block body %} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/Controller/TestController.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/Controller/TestController.php new file mode 100644 index 0000000000000..3effab9b59913 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/Controller/TestController.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\JsonLoginBundle\Controller; + +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\Security\Core\User\UserInterface; + +/** + * @author Kévin Dunglas + */ +class TestController +{ + public function loginCheckAction(UserInterface $user) + { + return new JsonResponse(array('message' => sprintf('Welcome @%s!', $user->getUsername()))); + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/JsonLoginBundle.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/JsonLoginBundle.php new file mode 100644 index 0000000000000..88b57859ad24c --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/JsonLoginBundle.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\JsonLoginBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +/** + * @author Kévin Dunglas + */ +class JsonLoginBundle extends Bundle +{ +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/Security/Http/JsonAuthenticationFailureHandler.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/Security/Http/JsonAuthenticationFailureHandler.php new file mode 100644 index 0000000000000..fbd482ddffeb0 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/Security/Http/JsonAuthenticationFailureHandler.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\JsonLoginBundle\Security\Http; + +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; + +class JsonAuthenticationFailureHandler implements AuthenticationFailureHandlerInterface +{ + public function onAuthenticationFailure(Request $request, AuthenticationException $exception) + { + return new JsonResponse(array('message' => 'Something went wrong'), 500); + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/Security/Http/JsonAuthenticationSuccessHandler.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/Security/Http/JsonAuthenticationSuccessHandler.php new file mode 100644 index 0000000000000..0e65bbb56b98a --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/Security/Http/JsonAuthenticationSuccessHandler.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\JsonLoginBundle\Security\Http; + +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; + +class JsonAuthenticationSuccessHandler implements AuthenticationSuccessHandlerInterface +{ + public function onAuthenticationSuccess(Request $request, TokenInterface $token) + { + return new JsonResponse(array('message' => sprintf('Good game @%s!', $token->getUsername()))); + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php index 80211f5251b08..2d95d35065137 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php @@ -108,14 +108,4 @@ public function getConfigs() array('routes_as_path.yml'), ); } - - public static function setUpBeforeClass() - { - parent::deleteTmpDir('CsrfFormLogin'); - } - - public static function tearDownAfterClass() - { - parent::deleteTmpDir('CsrfFormLogin'); - } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FirewallEntryPointTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FirewallEntryPointTest.php index 30d0935ffddd7..8179c2e942a6c 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FirewallEntryPointTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FirewallEntryPointTest.php @@ -43,14 +43,4 @@ public function testItUsesTheConfiguredEntryPointFromTheExceptionListenerWithFor "Custom entry point wasn't started" ); } - - public static function setUpBeforeClass() - { - parent::deleteTmpDir('FirewallEntryPoint'); - } - - public static function tearDownAfterClass() - { - parent::deleteTmpDir('FirewallEntryPoint'); - } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FormLoginTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FormLoginTest.php index 2f19f3f8a1a9a..c2e766e9acca1 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FormLoginTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FormLoginTest.php @@ -113,14 +113,4 @@ public function getConfigs() array('routes_as_path.yml'), ); } - - public static function setUpBeforeClass() - { - parent::deleteTmpDir('StandardFormLogin'); - } - - public static function tearDownAfterClass() - { - parent::deleteTmpDir('StandardFormLogin'); - } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/JsonLoginTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/JsonLoginTest.php new file mode 100644 index 0000000000000..7ee4c9b25dd45 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/JsonLoginTest.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional; + +use Symfony\Component\HttpFoundation\JsonResponse; + +/** + * @author Kévin Dunglas + */ +class JsonLoginTest extends WebTestCase +{ + public function testDefaultJsonLoginSuccess() + { + $client = $this->createClient(array('test_case' => 'JsonLogin', 'root_config' => 'config.yml')); + $client->request('POST', '/chk', array(), array(), array('CONTENT_TYPE' => 'application/json'), '{"user": {"login": "dunglas", "password": "foo"}}'); + $response = $client->getResponse(); + + $this->assertInstanceOf(JsonResponse::class, $response); + $this->assertSame(200, $response->getStatusCode()); + $this->assertSame(array('message' => 'Welcome @dunglas!'), json_decode($response->getContent(), true)); + } + + public function testDefaultJsonLoginFailure() + { + $client = $this->createClient(array('test_case' => 'JsonLogin', 'root_config' => 'config.yml')); + $client->request('POST', '/chk', array(), array(), array('CONTENT_TYPE' => 'application/json'), '{"user": {"login": "dunglas", "password": "bad"}}'); + $response = $client->getResponse(); + + $this->assertInstanceOf(JsonResponse::class, $response); + $this->assertSame(401, $response->getStatusCode()); + $this->assertSame(array('error' => 'Invalid credentials.'), json_decode($response->getContent(), true)); + } + + public function testCustomJsonLoginSuccess() + { + $client = $this->createClient(array('test_case' => 'JsonLogin', 'root_config' => 'custom_handlers.yml')); + $client->request('POST', '/chk', array(), array(), array('CONTENT_TYPE' => 'application/json'), '{"user": {"login": "dunglas", "password": "foo"}}'); + $response = $client->getResponse(); + + $this->assertInstanceOf(JsonResponse::class, $response); + $this->assertSame(200, $response->getStatusCode()); + $this->assertSame(array('message' => 'Good game @dunglas!'), json_decode($response->getContent(), true)); + } + + public function testCustomJsonLoginFailure() + { + $client = $this->createClient(array('test_case' => 'JsonLogin', 'root_config' => 'custom_handlers.yml')); + $client->request('POST', '/chk', array(), array(), array('CONTENT_TYPE' => 'application/json'), '{"user": {"login": "dunglas", "password": "bad"}}'); + $response = $client->getResponse(); + + $this->assertInstanceOf(JsonResponse::class, $response); + $this->assertSame(500, $response->getStatusCode()); + $this->assertSame(array('message' => 'Something went wrong'), json_decode($response->getContent(), true)); + } + + public function testDefaultJsonLoginBadRequest() + { + $client = $this->createClient(array('test_case' => 'JsonLogin', 'root_config' => 'config.yml')); + $client->request('POST', '/chk', array(), array(), array('CONTENT_TYPE' => 'application/json'), 'Not a json content'); + $response = $client->getResponse(); + + $this->assertSame(400, $response->getStatusCode()); + $this->assertSame('application/json', $response->headers->get('Content-Type')); + $this->assertArraySubset(array('error' => array('code' => 400, 'message' => 'Bad Request')), json_decode($response->getContent(), true)); + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php index 14c317966e21a..cb600764eaf42 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php @@ -76,14 +76,4 @@ public function getLocales() { return array(array('en'), array('de')); } - - public static function setUpBeforeClass() - { - parent::deleteTmpDir('StandardFormLogin'); - } - - public static function tearDownAfterClass() - { - parent::deleteTmpDir('StandardFormLogin'); - } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php index c7db437819a1c..3abfd9585f159 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php @@ -116,14 +116,4 @@ public function getConfigs() { return array(array('config.yml'), array('routes_as_path.yml')); } - - public static function setUpBeforeClass() - { - parent::deleteTmpDir('StandardFormLogin'); - } - - public static function tearDownAfterClass() - { - parent::deleteTmpDir('StandardFormLogin'); - } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SetAclCommandTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SetAclCommandTest.php index db4c51c5f064d..3b060a86fc242 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SetAclCommandTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SetAclCommandTest.php @@ -40,20 +40,9 @@ class SetAclCommandTest extends WebTestCase const OBJECT_CLASS = 'Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AclBundle\Entity\Car'; const SECURITY_CLASS = 'Symfony\Component\Security\Core\User\User'; - protected function setUp() - { - parent::setUp(); - - $this->deleteTmpDir('Acl'); - } - - protected function tearDown() - { - parent::tearDown(); - - $this->deleteTmpDir('Acl'); - } - + /** + * @group legacy + */ public function testSetAclUser() { $objectId = 1; diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SwitchUserTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SwitchUserTest.php index e5079c3283aac..f12e95671be2c 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SwitchUserTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SwitchUserTest.php @@ -70,14 +70,4 @@ protected function createAuthenticatedClient($username) return $client; } - - public static function setUpBeforeClass() - { - parent::deleteTmpDir('StandardFormLogin'); - } - - public static function tearDownAfterClass() - { - parent::deleteTmpDir('StandardFormLogin'); - } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php index b1c75bfedd755..0710f94b5b094 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php @@ -13,8 +13,10 @@ use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Bundle\SecurityBundle\Command\UserPasswordEncoderCommand; +use Symfony\Component\Console\Application as ConsoleApplication; use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder; +use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; use Symfony\Component\Security\Core\Encoder\Pbkdf2PasswordEncoder; /** @@ -24,6 +26,7 @@ */ class UserPasswordEncoderCommandTest extends WebTestCase { + /** @var CommandTester */ private $passwordEncoderCommandTester; public function testEncodePasswordEmptySalt() @@ -105,6 +108,7 @@ public function testEncodePasswordEmptySaltOutput() array( 'command' => 'security:encode-password', 'password' => 'p@ssw0rd', + 'user-class' => 'Symfony\Component\Security\Core\User\User', '--empty-salt' => true, ) ); @@ -129,7 +133,12 @@ public function testEncodePasswordBcryptOutput() public function testEncodePasswordNoConfigForGivenUserClass() { - $this->setExpectedException('\RuntimeException', 'No encoder has been configured for account "Foo\Bar\User".'); + if (method_exists($this, 'expectException')) { + $this->expectException('\RuntimeException'); + $this->expectExceptionMessage('No encoder has been configured for account "Foo\Bar\User".'); + } else { + $this->setExpectedException('\RuntimeException', 'No encoder has been configured for account "Foo\Bar\User".'); + } $this->passwordEncoderCommandTester->execute(array( 'command' => 'security:encode-password', @@ -138,6 +147,74 @@ public function testEncodePasswordNoConfigForGivenUserClass() ), array('interactive' => false)); } + public function testEncodePasswordAsksNonProvidedUserClass() + { + $this->passwordEncoderCommandTester->setInputs(array('Custom\Class\Pbkdf2\User', "\n")); + $this->passwordEncoderCommandTester->execute(array( + 'command' => 'security:encode-password', + 'password' => 'password', + ), array('decorated' => false)); + + $this->assertContains(<<passwordEncoderCommandTester->getDisplay(true)); + } + + public function testNonInteractiveEncodePasswordUsesFirstUserClass() + { + $this->passwordEncoderCommandTester->execute(array( + 'command' => 'security:encode-password', + 'password' => 'password', + ), array('interactive' => false)); + + $this->assertContains('Encoder used Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder', $this->passwordEncoderCommandTester->getDisplay()); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage There are no configured encoders for the "security" extension. + */ + public function testThrowsExceptionOnNoConfiguredEncoders() + { + $application = new ConsoleApplication(); + $application->add(new UserPasswordEncoderCommand($this->getMockBuilder(EncoderFactoryInterface::class)->getMock(), array())); + + $passwordEncoderCommand = $application->find('security:encode-password'); + + $tester = new CommandTester($passwordEncoderCommand); + $tester->execute(array( + 'command' => 'security:encode-password', + 'password' => 'password', + ), array('interactive' => false)); + } + + /** + * @group legacy + * @expectedDeprecation Passing null as the first argument of "Symfony\Bundle\SecurityBundle\Command\UserPasswordEncoderCommand::__construct" is deprecated since version 3.3 and will be removed in 4.0. If the command was registered by convention, make it a service instead. + */ + public function testLegacy() + { + $application = new ConsoleApplication(); + $application->add(new UserPasswordEncoderCommand()); + + $passwordEncoderCommand = $application->find('security:encode-password'); + self::bootKernel(array('test_case' => 'PasswordEncode')); + $passwordEncoderCommand->setContainer(self::$kernel->getContainer()); + + $tester = new CommandTester($passwordEncoderCommand); + $tester->execute(array( + 'command' => 'security:encode-password', + 'password' => 'password', + ), array('interactive' => false)); + + $this->assertContains('Encoder used Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder', $tester->getDisplay()); + } + protected function setUp() { putenv('COLUMNS='.(119 + strlen(PHP_EOL))); @@ -146,8 +223,7 @@ protected function setUp() $application = new Application($kernel); - $application->add(new UserPasswordEncoderCommand()); - $passwordEncoderCommand = $application->find('security:encode-password'); + $passwordEncoderCommand = $application->get('security:encode-password'); $this->passwordEncoderCommandTester = new CommandTester($passwordEncoderCommand); } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/WebTestCase.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/WebTestCase.php index 33da9028a3daf..8bace799a37a8 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/WebTestCase.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/WebTestCase.php @@ -13,7 +13,6 @@ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase as BaseWebTestCase; use Symfony\Component\Filesystem\Filesystem; -use Symfony\Component\HttpKernel\Kernel; class WebTestCase extends BaseWebTestCase { @@ -23,9 +22,19 @@ public static function assertRedirect($response, $location) self::assertEquals('http://localhost'.$location, $response->headers->get('Location')); } - protected static function deleteTmpDir($testCase) + public static function setUpBeforeClass() { - if (!file_exists($dir = sys_get_temp_dir().'/'.Kernel::VERSION.'/'.$testCase)) { + static::deleteTmpDir(); + } + + public static function tearDownAfterClass() + { + static::deleteTmpDir(); + } + + protected static function deleteTmpDir() + { + if (!file_exists($dir = sys_get_temp_dir().'/'.static::getVarDir())) { return; } @@ -49,10 +58,16 @@ protected static function createKernel(array $options = array()) } return new $class( + static::getVarDir(), $options['test_case'], isset($options['root_config']) ? $options['root_config'] : 'config.yml', - isset($options['environment']) ? $options['environment'] : 'securitybundletest'.strtolower($options['test_case']), + isset($options['environment']) ? $options['environment'] : strtolower(static::getVarDir().$options['test_case']), isset($options['debug']) ? $options['debug'] : true ); } + + protected static function getVarDir() + { + return substr(strrchr(get_called_class(), '\\'), 1); + } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php index b828c5acfd91b..1aab514f45450 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php @@ -11,30 +11,6 @@ namespace Symfony\Bundle\SecurityBundle\Tests\Functional\app; -// get the autoload file -$dir = __DIR__; -$lastDir = null; -while ($dir !== $lastDir) { - $lastDir = $dir; - - if (is_file($dir.'/autoload.php')) { - require_once $dir.'/autoload.php'; - break; - } - - if (is_file($dir.'/autoload.php.dist')) { - require_once $dir.'/autoload.php.dist'; - break; - } - - if (file_exists($dir.'/vendor/autoload.php')) { - require_once $dir.'/vendor/autoload.php'; - break; - } - - $dir = dirname($dir); -} - use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\HttpKernel\Kernel; @@ -46,14 +22,16 @@ */ class AppKernel extends Kernel { + private $varDir; private $testCase; private $rootConfig; - public function __construct($testCase, $rootConfig, $environment, $debug) + public function __construct($varDir, $testCase, $rootConfig, $environment, $debug) { if (!is_dir(__DIR__.'/'.$testCase)) { throw new \InvalidArgumentException(sprintf('The test case "%s" does not exist.', $testCase)); } + $this->varDir = $varDir; $this->testCase = $testCase; $fs = new Filesystem(); @@ -71,7 +49,7 @@ public function __construct($testCase, $rootConfig, $environment, $debug) public function getName() { if (null === $this->name) { - $this->name = parent::getName().md5($this->rootConfig); + $this->name = parent::getName().substr(md5($this->rootConfig), -16); } return $this->name; @@ -93,12 +71,12 @@ public function getRootDir() public function getCacheDir() { - return sys_get_temp_dir().'/'.Kernel::VERSION.'/'.$this->testCase.'/cache/'.$this->environment; + return sys_get_temp_dir().'/'.$this->varDir.'/'.$this->testCase.'/cache/'.$this->environment; } public function getLogDir() { - return sys_get_temp_dir().'/'.Kernel::VERSION.'/'.$this->testCase.'/logs'; + return sys_get_temp_dir().'/'.$this->varDir.'/'.$this->testCase.'/logs'; } public function registerContainerConfiguration(LoaderInterface $loader) @@ -108,13 +86,13 @@ public function registerContainerConfiguration(LoaderInterface $loader) public function serialize() { - return serialize(array($this->testCase, $this->rootConfig, $this->getEnvironment(), $this->isDebug())); + return serialize(array($this->varDir, $this->testCase, $this->rootConfig, $this->getEnvironment(), $this->isDebug())); } public function unserialize($str) { $a = unserialize($str); - $this->__construct($a[0], $a[1], $a[2], $a[3]); + $this->__construct($a[0], $a[1], $a[2], $a[3], $a[4]); } protected function getKernelParameters() diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AutowiringTypes/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AutowiringTypes/bundles.php new file mode 100644 index 0000000000000..68e5afb125f85 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AutowiringTypes/bundles.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + new Symfony\Bundle\FrameworkBundle\FrameworkBundle(), + new Symfony\Bundle\SecurityBundle\SecurityBundle(), + new Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AutowiringBundle\AutowiringBundle(), +); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AutowiringTypes/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AutowiringTypes/config.yml new file mode 100644 index 0000000000000..bb3ef5a2dc70f --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AutowiringTypes/config.yml @@ -0,0 +1,14 @@ +imports: + - { resource: ../config/framework.yml } + +services: + test.autowiring_types.autowired_services: + class: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AutowiringBundle\AutowiredServices + autowire: true +security: + providers: + dummy: + memory: ~ + firewalls: + dummy: + security: false diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/bundles.php new file mode 100644 index 0000000000000..470ffff088cd9 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/bundles.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + new Symfony\Bundle\SecurityBundle\SecurityBundle(), + new Symfony\Bundle\FrameworkBundle\FrameworkBundle(), + new Symfony\Bundle\TwigBundle\TwigBundle(), + new Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\JsonLoginBundle\JsonLoginBundle(), +); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/config.yml new file mode 100644 index 0000000000000..d6ed10e896ff9 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/config.yml @@ -0,0 +1,24 @@ +imports: + - { resource: ./../config/framework.yml } + +security: + encoders: + Symfony\Component\Security\Core\User\User: plaintext + + providers: + in_memory: + memory: + users: + dunglas: { password: foo, roles: [ROLE_USER] } + + firewalls: + main: + pattern: ^/ + anonymous: true + json_login: + check_path: /chk + username_path: user.login + password_path: user.password + + access_control: + - { path: ^/foo, roles: ROLE_USER } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/custom_handlers.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/custom_handlers.yml new file mode 100644 index 0000000000000..e15e203c626cc --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/custom_handlers.yml @@ -0,0 +1,32 @@ +imports: + - { resource: ./../config/framework.yml } + +security: + encoders: + Symfony\Component\Security\Core\User\User: plaintext + + providers: + in_memory: + memory: + users: + dunglas: { password: foo, roles: [ROLE_USER] } + + firewalls: + main: + pattern: ^/ + anonymous: true + json_login: + check_path: /chk + username_path: user.login + password_path: user.password + success_handler: json_login.success_handler + failure_handler: json_login.failure_handler + + access_control: + - { path: ^/foo, roles: ROLE_USER } + +services: + json_login.success_handler: + class: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\JsonLoginBundle\Security\Http\JsonAuthenticationSuccessHandler + json_login.failure_handler: + class: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\JsonLoginBundle\Security\Http\JsonAuthenticationFailureHandler diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/routing.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/routing.yml new file mode 100644 index 0000000000000..ee49b4829bdd7 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/routing.yml @@ -0,0 +1,3 @@ +login_check: + path: /chk + defaults: { _controller: JsonLoginBundle:Test:loginCheck } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/config/twig.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/config/twig.yml index 61c1a54d99ba5..493989866a278 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/config/twig.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/config/twig.yml @@ -1,6 +1,3 @@ -framework: - templating: { engines: ['twig'] } - # Twig Configuration twig: debug: '%kernel.debug%' diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallConfigTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallConfigTest.php index a6fdeeed3768a..a0b4a79c50cdc 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallConfigTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallConfigTest.php @@ -11,9 +11,10 @@ namespace Symfony\Bundle\SecurityBundle\Tests\Security; +use PHPUnit\Framework\TestCase; use Symfony\Bundle\SecurityBundle\Security\FirewallConfig; -class FirewallConfigTest extends \PHPUnit_Framework_TestCase +class FirewallConfigTest extends TestCase { public function testGetters() { diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallContextTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallContextTest.php index e0790bf23c776..22be0fd081655 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallContextTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallContextTest.php @@ -11,22 +11,18 @@ namespace Symfony\Bundle\SecurityBundle\Tests\Security; +use PHPUnit\Framework\TestCase; use Symfony\Bundle\SecurityBundle\Security\FirewallConfig; use Symfony\Bundle\SecurityBundle\Security\FirewallContext; use Symfony\Component\Security\Http\Firewall\ExceptionListener; use Symfony\Component\Security\Http\Firewall\ListenerInterface; -class FirewallContextTest extends \PHPUnit_Framework_TestCase +class FirewallContextTest extends TestCase { public function testGetters() { $config = new FirewallConfig('main', 'user_checker', 'request_matcher'); - - $exceptionListener = $this - ->getMockBuilder(ExceptionListener::class) - ->disableOriginalConstructor() - ->getMock(); - + $exceptionListener = $this->getExceptionListenerMock(); $listeners = array( $this ->getMockBuilder(ListenerInterface::class) @@ -36,7 +32,28 @@ public function testGetters() $context = new FirewallContext($listeners, $exceptionListener, $config); - $this->assertEquals(array($listeners, $exceptionListener), $context->getContext()); + $this->assertEquals($listeners, $context->getListeners()); + $this->assertEquals($exceptionListener, $context->getExceptionListener()); $this->assertEquals($config, $context->getConfig()); } + + /** + * @expectedDeprecation Method Symfony\Bundle\SecurityBundle\Security\FirewallContext::getContext() is deprecated since version 3.3 and will be removed in 4.0. Use Symfony\Bundle\SecurityBundle\Security\FirewallContext::getListeners/getExceptionListener() instead. + * @group legacy + */ + public function testGetContext() + { + $context = (new FirewallContext($listeners = array(), $exceptionListener = $this->getExceptionListenerMock(), new FirewallConfig('main', 'request_matcher', 'user_checker'))) + ->getContext(); + + $this->assertEquals(array($listeners, $exceptionListener), $context); + } + + private function getExceptionListenerMock() + { + return $this + ->getMockBuilder(ExceptionListener::class) + ->disableOriginalConstructor() + ->getMock(); + } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/SecurityUserValueResolverTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/SecurityUserValueResolverTest.php index e0d626a8cee40..0f9a0fe80b646 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/SecurityUserValueResolverTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/SecurityUserValueResolverTest.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\SecurityBundle\Tests; +use PHPUnit\Framework\TestCase; use Symfony\Bundle\SecurityBundle\SecurityUserValueResolver; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ArgumentResolver; @@ -20,7 +21,7 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\User\UserInterface; -class SecurityUserValueResolverTest extends \PHPUnit_Framework_TestCase +class SecurityUserValueResolverTest extends TestCase { public function testResolveNoToken() { @@ -33,8 +34,8 @@ public function testResolveNoToken() public function testResolveNoUser() { - $mock = $this->getMock(UserInterface::class); - $token = $this->getMock(TokenInterface::class); + $mock = $this->getMockBuilder(UserInterface::class)->getMock(); + $token = $this->getMockBuilder(TokenInterface::class)->getMock(); $tokenStorage = new TokenStorage(); $tokenStorage->setToken($token); @@ -55,8 +56,8 @@ public function testResolveWrongType() public function testResolve() { - $user = $this->getMock(UserInterface::class); - $token = $this->getMock(TokenInterface::class); + $user = $this->getMockBuilder(UserInterface::class)->getMock(); + $token = $this->getMockBuilder(TokenInterface::class)->getMock(); $token->expects($this->any())->method('getUser')->willReturn($user); $tokenStorage = new TokenStorage(); $tokenStorage->setToken($token); @@ -70,8 +71,8 @@ public function testResolve() public function testIntegration() { - $user = $this->getMock(UserInterface::class); - $token = $this->getMock(TokenInterface::class); + $user = $this->getMockBuilder(UserInterface::class)->getMock(); + $token = $this->getMockBuilder(TokenInterface::class)->getMock(); $token->expects($this->any())->method('getUser')->willReturn($user); $tokenStorage = new TokenStorage(); $tokenStorage->setToken($token); @@ -82,7 +83,7 @@ public function testIntegration() public function testIntegrationNoUser() { - $token = $this->getMock(TokenInterface::class); + $token = $this->getMockBuilder(TokenInterface::class)->getMock(); $tokenStorage = new TokenStorage(); $tokenStorage->setToken($token); diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index 132093452fe33..841fc3ea5d6b8 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -17,31 +17,35 @@ ], "require": { "php": ">=5.5.9", - "symfony/security": "~3.2", - "symfony/http-kernel": "~3.2", + "symfony/security": "~3.3", + "symfony/dependency-injection": "~3.3", + "symfony/http-kernel": "~3.3", "symfony/polyfill-php70": "~1.0" }, "require-dev": { "symfony/asset": "~2.8|~3.0", "symfony/browser-kit": "~2.8|~3.0", - "symfony/console": "~2.8|~3.0", + "symfony/console": "~3.2", "symfony/css-selector": "~2.8|~3.0", "symfony/dom-crawler": "~2.8|~3.0", - "symfony/form": "~2.8|~3.0", - "symfony/framework-bundle": "~3.1", + "symfony/form": "^2.8.18|^3.2.5", + "symfony/framework-bundle": "^3.2.8", "symfony/http-foundation": "~2.8|~3.0", "symfony/security-acl": "~2.8|~3.0", - "symfony/templating": "~2.8|~3.0", + "symfony/translation": "~2.8|~3.0", "symfony/twig-bundle": "~2.8|~3.0", "symfony/twig-bridge": "~2.8|~3.0", "symfony/process": "~2.8|~3.0", - "symfony/validator": "~2.8|~3.0", - "symfony/var-dumper": "~3.2", + "symfony/validator": "^3.2.5", + "symfony/var-dumper": "~3.3", "symfony/yaml": "~2.8|~3.0", "symfony/expression-language": "~2.8|~3.0", "doctrine/doctrine-bundle": "~1.4", "twig/twig": "~1.28|~2.0" }, + "conflict": { + "symfony/var-dumper": "<3.3" + }, "suggest": { "symfony/security-acl": "For using the ACL functionality of this bundle" }, @@ -54,7 +58,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.3-dev" } } } diff --git a/src/Symfony/Bundle/SecurityBundle/phpunit.xml.dist b/src/Symfony/Bundle/SecurityBundle/phpunit.xml.dist index a7fdc326c4571..8636f70c187a5 100644 --- a/src/Symfony/Bundle/SecurityBundle/phpunit.xml.dist +++ b/src/Symfony/Bundle/SecurityBundle/phpunit.xml.dist @@ -5,6 +5,8 @@ backupGlobals="false" colors="true" bootstrap="vendor/autoload.php" + failOnRisky="true" + failOnWarning="true" > diff --git a/src/Symfony/Bundle/TwigBundle/CHANGELOG.md b/src/Symfony/Bundle/TwigBundle/CHANGELOG.md index 90f7cbd1e0178..9f666dbc29dd7 100644 --- a/src/Symfony/Bundle/TwigBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/TwigBundle/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +3.3.0 +----- + + * Deprecated `ContainerAwareRuntimeLoader` + 2.7.0 ----- diff --git a/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheCacheWarmer.php b/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheCacheWarmer.php index 9eea901f6dde4..22184833f0eae 100644 --- a/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheCacheWarmer.php +++ b/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheCacheWarmer.php @@ -11,9 +11,10 @@ namespace Symfony\Bundle\TwigBundle\CacheWarmer; +use Psr\Container\ContainerInterface; +use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; use Symfony\Component\Finder\Finder; use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Bundle\FrameworkBundle\CacheWarmer\TemplateFinderInterface; use Symfony\Component\Templating\TemplateReference; @@ -25,7 +26,7 @@ * * @author Fabien Potencier */ -class TemplateCacheCacheWarmer implements CacheWarmerInterface +class TemplateCacheCacheWarmer implements CacheWarmerInterface, ServiceSubscriberInterface { protected $container; protected $finder; @@ -92,6 +93,16 @@ public function isOptional() return true; } + /** + * {@inheritdoc} + */ + public static function getSubscribedServices() + { + return array( + 'twig' => \Twig_Environment::class, + ); + } + /** * Find templates in the given directory. * diff --git a/src/Symfony/Bundle/TwigBundle/ContainerAwareRuntimeLoader.php b/src/Symfony/Bundle/TwigBundle/ContainerAwareRuntimeLoader.php index 780454aed986e..e2988f3aae5e0 100644 --- a/src/Symfony/Bundle/TwigBundle/ContainerAwareRuntimeLoader.php +++ b/src/Symfony/Bundle/TwigBundle/ContainerAwareRuntimeLoader.php @@ -11,6 +11,8 @@ namespace Symfony\Bundle\TwigBundle; +@trigger_error(sprintf('The %s class is deprecated since version 3.3 and will be removed in 4.0. Use the Twig_ContainerRuntimeLoader class instead.', ContainerAwareRuntimeLoader::class), E_USER_DEPRECATED); + use Psr\Log\LoggerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -18,6 +20,8 @@ * Loads Twig extension runtimes via the service container. * * @author Fabien Potencier + * + * @deprecated since version 3.3, will be removed in 4.0. Use \Twig_ContainerRuntimeLoader instead. */ class ContainerAwareRuntimeLoader implements \Twig_RuntimeLoaderInterface { diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExceptionListenerPass.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExceptionListenerPass.php index 9fbfd65950a53..b7ebb2a406512 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExceptionListenerPass.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExceptionListenerPass.php @@ -27,8 +27,10 @@ public function process(ContainerBuilder $container) return; } - // register the exception controller only if Twig is enabled - if ($container->hasParameter('templating.engines')) { + // register the exception controller only if Twig is enabled and required dependencies do exist + if (!class_exists('Symfony\Component\Debug\Exception\FlattenException') || !interface_exists('Symfony\Component\EventDispatcher\EventSubscriberInterface')) { + $container->removeDefinition('twig.exception_listener'); + } elseif ($container->hasParameter('templating.engines')) { $engines = $container->getParameter('templating.engines'); if (!in_array('twig', $engines)) { $container->removeDefinition('twig.exception_listener'); diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php index 603cbe8e22ef1..3e23f5a8a802f 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php @@ -11,13 +11,10 @@ namespace Symfony\Bundle\TwigBundle\DependencyInjection\Compiler; -use Symfony\Component\Config\Resource\ClassExistenceResource; +use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\ExpressionLanguage\ExpressionLanguage; -use Symfony\Component\Stopwatch\Stopwatch; -use Symfony\Component\Yaml\Parser as YamlParser; /** * @author Jean-François Simon @@ -26,10 +23,29 @@ class ExtensionPass implements CompilerPassInterface { public function process(ContainerBuilder $container) { + if (!class_exists('Symfony\Component\Asset\Packages')) { + $container->removeDefinition('twig.extension.assets'); + } + + if (!class_exists('Symfony\Component\ExpressionLanguage\Expression')) { + $container->removeDefinition('twig.extension.expression'); + } + + if (!interface_exists('Symfony\Component\Routing\Generator\UrlGeneratorInterface')) { + $container->removeDefinition('twig.extension.routing'); + } + if (!interface_exists('Symfony\Component\Translation\TranslatorInterface')) { + $container->removeDefinition('twig.extension.trans'); + } + + if (!class_exists('Symfony\Component\Yaml\Yaml')) { + $container->removeDefinition('twig.extension.yaml'); + } + if ($container->has('form.extension')) { $container->getDefinition('twig.extension.form')->addTag('twig.extension'); $reflClass = new \ReflectionClass('Symfony\Bridge\Twig\Extension\FormExtension'); - $container->getDefinition('twig.loader.filesystem')->addMethodCall('addPath', array(dirname(dirname($reflClass->getFileName())).'/Resources/views/Form')); + $container->getDefinition('twig.loader.native_filesystem')->addMethodCall('addPath', array(dirname(dirname($reflClass->getFileName())).'/Resources/views/Form')); } if ($container->has('translator')) { @@ -64,46 +80,30 @@ public function process(ContainerBuilder $container) $container->getDefinition('twig.extension.debug')->addTag('twig.extension'); } - if (!$container->has('templating')) { - $loader = $container->getDefinition('twig.loader.native_filesystem'); - $loader->replaceArgument(1, $this->getComposerRootDir($container->getParameter('kernel.root_dir'))); - $loader->addTag('twig.loader'); - $loader->setMethodCalls($container->getDefinition('twig.loader.filesystem')->getMethodCalls()); + $twigLoader = $container->getDefinition('twig.loader.native_filesystem'); + if ($container->has('templating')) { + $loader = $container->getDefinition('twig.loader.filesystem'); + $loader->setMethodCalls(array_merge($twigLoader->getMethodCalls(), $loader->getMethodCalls())); - $container->setDefinition('twig.loader.filesystem', $loader); + $twigLoader->clearTag('twig.loader'); + } else { + $container->setAlias('twig.loader.filesystem', new Alias('twig.loader.native_filesystem', false)); } if ($container->has('assets.packages')) { $container->getDefinition('twig.extension.assets')->addTag('twig.extension'); } - $container->addResource(new ClassExistenceResource(YamlParser::class)); - if (class_exists(YamlParser::class)) { + if ($container->hasDefinition('twig.extension.yaml')) { $container->getDefinition('twig.extension.yaml')->addTag('twig.extension'); } - $container->addResource(new ClassExistenceResource(Stopwatch::class)); - if (class_exists(Stopwatch::class)) { + if (class_exists('Symfony\Component\Stopwatch\Stopwatch')) { $container->getDefinition('twig.extension.debug.stopwatch')->addTag('twig.extension'); } - $container->addResource(new ClassExistenceResource(ExpressionLanguage::class)); - if (class_exists(ExpressionLanguage::class)) { + if ($container->hasDefinition('twig.extension.expression')) { $container->getDefinition('twig.extension.expression')->addTag('twig.extension'); } } - - private function getComposerRootDir($rootDir) - { - $dir = $rootDir; - while (!file_exists($dir.'/composer.json')) { - if ($dir === dirname($dir)) { - return $rootDir; - } - - $dir = dirname($dir); - } - - return $dir; - } } diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/RuntimeLoaderPass.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/RuntimeLoaderPass.php index 6a4c1f269053c..a4defc51ff33a 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/RuntimeLoaderPass.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/RuntimeLoaderPass.php @@ -13,6 +13,8 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; +use Symfony\Component\DependencyInjection\Reference; /** * Registers Twig runtime services. @@ -27,20 +29,11 @@ public function process(ContainerBuilder $container) $definition = $container->getDefinition('twig.runtime_loader'); $mapping = array(); - foreach ($container->findTaggedServiceIds('twig.runtime') as $id => $attributes) { + foreach ($container->findTaggedServiceIds('twig.runtime', true) as $id => $attributes) { $def = $container->getDefinition($id); - - if (!$def->isPublic()) { - throw new InvalidArgumentException(sprintf('The service "%s" must be public as it can be lazy-loaded.', $id)); - } - - if ($def->isAbstract()) { - throw new InvalidArgumentException(sprintf('The service "%s" must not be abstract as it can be lazy-loaded.', $id)); - } - - $mapping[$def->getClass()] = $id; + $mapping[$def->getClass()] = new Reference($id); } - $definition->replaceArgument(1, $mapping); + $definition->replaceArgument(0, ServiceLocatorTagPass::register($container, $mapping)); } } diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php index 927e4b46e18ac..f520ab11f0096 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php @@ -36,7 +36,7 @@ public function process(ContainerBuilder $container) // be registered. $calls = $definition->getMethodCalls(); $definition->setMethodCalls(array()); - foreach ($container->findTaggedServiceIds('twig.extension') as $id => $attributes) { + foreach ($container->findTaggedServiceIds('twig.extension', true) as $id => $attributes) { $definition->addMethodCall('addExtension', array(new Reference($id))); } $definition->setMethodCalls(array_merge($definition->getMethodCalls(), $calls)); diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigLoaderPass.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigLoaderPass.php index bc3b71c696ed7..f2c1d32eec974 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigLoaderPass.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigLoaderPass.php @@ -29,27 +29,23 @@ public function process(ContainerBuilder $container) return; } - // register additional template loaders - $loaderIds = $container->findTaggedServiceIds('twig.loader'); + $prioritizedLoaders = array(); + $found = 0; - if (count($loaderIds) === 0) { + foreach ($container->findTaggedServiceIds('twig.loader', true) as $id => $attributes) { + $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0; + $prioritizedLoaders[$priority][] = $id; + ++$found; + } + + if (!$found) { throw new LogicException('No twig loaders found. You need to tag at least one loader with "twig.loader"'); } - if (count($loaderIds) === 1) { - $container->setAlias('twig.loader', key($loaderIds)); + if (1 === $found) { + $container->setAlias('twig.loader', $id); } else { $chainLoader = $container->getDefinition('twig.loader.chain'); - - $prioritizedLoaders = array(); - - foreach ($loaderIds as $id => $tags) { - foreach ($tags as $tag) { - $priority = isset($tag['priority']) ? $tag['priority'] : 0; - $prioritizedLoaders[$priority][] = $id; - } - } - krsort($prioritizedLoaders); foreach ($prioritizedLoaders as $loaders) { diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php index 5296ac542ca87..8e37cbba871b8 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php @@ -11,12 +11,14 @@ namespace Symfony\Bundle\TwigBundle\DependencyInjection; +use Symfony\Bridge\Twig\Extension\WebLinkExtension; use Symfony\Component\Config\FileLocator; -use Symfony\Component\Config\Resource\FileExistenceResource; +use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\WebLink\HttpHeaderSerializer; /** * TwigExtension. @@ -37,6 +39,24 @@ public function load(array $configs, ContainerBuilder $container) $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('twig.xml'); + if (class_exists('Symfony\Component\Form\Form')) { + $loader->load('form.xml'); + } + + if (interface_exists('Symfony\Component\Templating\EngineInterface')) { + $loader->load('templating.xml'); + } + + if (!interface_exists('Symfony\Component\Translation\TranslatorInterface')) { + $container->removeDefinition('twig.translation.extractor'); + } + + if (class_exists(HttpHeaderSerializer::class)) { + $definition = $container->register('twig.extension.weblink', WebLinkExtension::class); + $definition->setPublic(false); + $definition->addArgument(new Reference('request_stack')); + } + foreach ($configs as $key => $config) { if (isset($config['globals'])) { foreach ($config['globals'] as $name => $value) { @@ -66,7 +86,7 @@ public function load(array $configs, ContainerBuilder $container) $envConfiguratorDefinition->replaceArgument(4, $config['number_format']['decimal_point']); $envConfiguratorDefinition->replaceArgument(5, $config['number_format']['thousands_separator']); - $twigFilesystemLoaderDefinition = $container->getDefinition('twig.loader.filesystem'); + $twigFilesystemLoaderDefinition = $container->getDefinition('twig.loader.native_filesystem'); // register user-configured paths foreach ($config['paths'] as $path => $namespace) { @@ -80,27 +100,25 @@ public function load(array $configs, ContainerBuilder $container) $container->getDefinition('twig.cache_warmer')->replaceArgument(2, $config['paths']); $container->getDefinition('twig.template_iterator')->replaceArgument(2, $config['paths']); - // register bundles as Twig namespaces - foreach ($container->getParameter('kernel.bundles') as $bundle => $class) { - $dir = $container->getParameter('kernel.root_dir').'/Resources/'.$bundle.'/views'; - if (is_dir($dir)) { - $this->addTwigPath($twigFilesystemLoaderDefinition, $dir, $bundle); + $bundleHierarchy = $this->getBundleHierarchy($container); + + foreach ($bundleHierarchy as $name => $bundle) { + $namespace = $this->normalizeBundleName($name); + + foreach ($bundle['children'] as $child) { + foreach ($bundleHierarchy[$child]['paths'] as $path) { + $twigFilesystemLoaderDefinition->addMethodCall('addPath', array($path, $namespace)); + } } - $container->addResource(new FileExistenceResource($dir)); - $reflection = new \ReflectionClass($class); - $dir = dirname($reflection->getFileName()).'/Resources/views'; - if (is_dir($dir)) { - $this->addTwigPath($twigFilesystemLoaderDefinition, $dir, $bundle); + foreach ($bundle['paths'] as $path) { + $twigFilesystemLoaderDefinition->addMethodCall('addPath', array($path, $namespace)); } - $container->addResource(new FileExistenceResource($dir)); } - $dir = $container->getParameter('kernel.root_dir').'/Resources/views'; - if (is_dir($dir)) { + if ($container->fileExists($dir = $container->getParameter('kernel.root_dir').'/Resources/views', false)) { $twigFilesystemLoaderDefinition->addMethodCall('addPath', array($dir)); } - $container->addResource(new FileExistenceResource($dir)); if (!empty($config['globals'])) { $def = $container->getDefinition('twig'); @@ -126,25 +144,82 @@ public function load(array $configs, ContainerBuilder $container) $container->getDefinition('twig')->replaceArgument(1, $config); - $this->addClassesToCompile(array( - 'Twig_Environment', - 'Twig_Extension', - 'Twig_Extension_Core', - 'Twig_Extension_Escaper', - 'Twig_Extension_Optimizer', - 'Twig_LoaderInterface', - 'Twig_Markup', - 'Twig_Template', - )); + $container->registerForAutoconfiguration(\Twig_ExtensionInterface::class) + ->addTag('twig.extension'); + $container->registerForAutoconfiguration(\Twig_LoaderInterface::class) + ->addTag('twig.loader'); + + if (PHP_VERSION_ID < 70000) { + $this->addClassesToCompile(array( + 'Twig_Environment', + 'Twig_Extension', + 'Twig_Extension_Core', + 'Twig_Extension_Escaper', + 'Twig_Extension_Optimizer', + 'Twig_LoaderInterface', + 'Twig_Markup', + 'Twig_Template', + )); + } + } + + private function getBundleHierarchy(ContainerBuilder $container) + { + $bundleHierarchy = array(); + + foreach ($container->getParameter('kernel.bundles_metadata') as $name => $bundle) { + if (!array_key_exists($name, $bundleHierarchy)) { + $bundleHierarchy[$name] = array( + 'paths' => array(), + 'parents' => array(), + 'children' => array(), + ); + } + + if ($container->fileExists($dir = $container->getParameter('kernel.root_dir').'/Resources/'.$name.'/views', false)) { + $bundleHierarchy[$name]['paths'][] = $dir; + } + + if ($container->fileExists($dir = $bundle['path'].'/Resources/views', false)) { + $bundleHierarchy[$name]['paths'][] = $dir; + } + + if (null === $bundle['parent']) { + continue; + } + + $bundleHierarchy[$name]['parents'][] = $bundle['parent']; + + if (!array_key_exists($bundle['parent'], $bundleHierarchy)) { + $bundleHierarchy[$bundle['parent']] = array( + 'paths' => array(), + 'parents' => array(), + 'children' => array(), + ); + } + + $bundleHierarchy[$bundle['parent']]['children'] = array_merge($bundleHierarchy[$name]['children'], array($name), $bundleHierarchy[$bundle['parent']]['children']); + + foreach ($bundleHierarchy[$bundle['parent']]['parents'] as $parent) { + $bundleHierarchy[$name]['parents'][] = $parent; + $bundleHierarchy[$parent]['children'] = array_merge($bundleHierarchy[$name]['children'], array($name), $bundleHierarchy[$parent]['children']); + } + + foreach ($bundleHierarchy[$name]['children'] as $child) { + $bundleHierarchy[$child]['parents'] = array_merge($bundleHierarchy[$child]['parents'], $bundleHierarchy[$name]['parents']); + } + } + + return $bundleHierarchy; } - private function addTwigPath($twigFilesystemLoaderDefinition, $dir, $bundle) + private function normalizeBundleName($name) { - $name = $bundle; if ('Bundle' === substr($name, -6)) { $name = substr($name, 0, -6); } - $twigFilesystemLoaderDefinition->addMethodCall('addPath', array($dir, $name)); + + return $name; } /** diff --git a/src/Symfony/Bundle/TwigBundle/LICENSE b/src/Symfony/Bundle/TwigBundle/LICENSE index 12a74531e40a4..17d16a13367dd 100644 --- a/src/Symfony/Bundle/TwigBundle/LICENSE +++ b/src/Symfony/Bundle/TwigBundle/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2016 Fabien Potencier +Copyright (c) 2004-2017 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bundle/TwigBundle/Loader/FilesystemLoader.php b/src/Symfony/Bundle/TwigBundle/Loader/FilesystemLoader.php index 53fe300e29a62..0a9ac7a3ca19a 100644 --- a/src/Symfony/Bundle/TwigBundle/Loader/FilesystemLoader.php +++ b/src/Symfony/Bundle/TwigBundle/Loader/FilesystemLoader.php @@ -29,12 +29,13 @@ class FilesystemLoader extends \Twig_Loader_Filesystem /** * Constructor. * - * @param FileLocatorInterface $locator A FileLocatorInterface instance - * @param TemplateNameParserInterface $parser A TemplateNameParserInterface instance + * @param FileLocatorInterface $locator A FileLocatorInterface instance + * @param TemplateNameParserInterface $parser A TemplateNameParserInterface instance + * @param string|null $rootPath The root path common to all relative paths (null for getcwd()) */ - public function __construct(FileLocatorInterface $locator, TemplateNameParserInterface $parser) + public function __construct(FileLocatorInterface $locator, TemplateNameParserInterface $parser, $rootPath = null) { - parent::__construct(array()); + parent::__construct(array(), $rootPath); $this->locator = $locator; $this->parser = $parser; diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/form.xml b/src/Symfony/Bundle/TwigBundle/Resources/config/form.xml new file mode 100644 index 0000000000000..f1603c15461bd --- /dev/null +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/form.xml @@ -0,0 +1,25 @@ + + + + + + + + twig.form.renderer + + + + + %twig.form.resources% + + + + + + + + + + diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/templating.xml b/src/Symfony/Bundle/TwigBundle/Resources/config/templating.xml new file mode 100644 index 0000000000000..b9c1806221ed0 --- /dev/null +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/templating.xml @@ -0,0 +1,20 @@ + + + + + + + + %kernel.project_dir% + + + + + + + + + + diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml b/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml index c82cbafbeb953..22ad287f70113 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml @@ -17,6 +17,7 @@
+ %kernel.environment% @@ -27,7 +28,8 @@ - + + @@ -46,25 +48,12 @@ - - - - - - + %kernel.project_dir% - - - - - - - - @@ -87,7 +76,7 @@ - + %kernel.root_dir% %kernel.charset% @@ -117,21 +106,8 @@ - - - - %twig.form.resources% - - - - - - - - - @@ -163,10 +139,8 @@ - - - - + +
diff --git a/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.atom.twig b/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.atom.twig index c27cc56e6a078..25c84a6c9b5ec 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.atom.twig +++ b/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.atom.twig @@ -1 +1 @@ -{% include '@Twig/Exception/error.xml.twig' %} +{{ include('@Twig/Exception/error.xml.twig') }} diff --git a/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.rdf.twig b/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.rdf.twig index c27cc56e6a078..25c84a6c9b5ec 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.rdf.twig +++ b/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.rdf.twig @@ -1 +1 @@ -{% include '@Twig/Exception/error.xml.twig' %} +{{ include('@Twig/Exception/error.xml.twig') }} diff --git a/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.atom.twig b/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.atom.twig index d507ce46f6949..2cdf03f2bcb59 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.atom.twig +++ b/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.atom.twig @@ -1 +1 @@ -{% include '@Twig/Exception/exception.xml.twig' with { 'exception': exception } %} +{{ include('@Twig/Exception/exception.xml.twig', { exception: exception }) }} diff --git a/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.css.twig b/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.css.twig index bdf242b7f1998..593d490257e35 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.css.twig +++ b/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.css.twig @@ -1,3 +1,3 @@ /* -{% include '@Twig/Exception/exception.txt.twig' with { 'exception': exception } %} +{{ include('@Twig/Exception/exception.txt.twig', { exception: exception }) }} */ diff --git a/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.html.twig b/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.html.twig index f405f6473e8f5..4b35e4fad77db 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.html.twig +++ b/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.html.twig @@ -1,122 +1,99 @@ -
-
- -
- {{ include('@Twig/Exception/exception.svg') }} +
+