diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index d35bbdeb69e23..cb9b8a69c1638 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,6 @@ | Q | A | ------------- | --- -| Branch? | master / 2.7, 2.8 or 3.2 +| Branch? | 3.4 or master / 2.7, 2.8, 3.2 or 3.3 | Bug fix? | yes/no | New feature? | yes/no | BC breaks? | yes/no @@ -13,7 +13,8 @@ diff --git a/.travis.yml b/.travis.yml index 6668279015c13..4e9aad6b4669b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -83,8 +83,12 @@ before_install: 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 + if [[ $PHP = 5.* ]]; then + echo extension = mongo.so >> $INI + elif [[ $PHP = 7.* ]]; then + echo extension = mongodb.so >> $INI + fi # Matrix lines for intermediate PHP versions are skipped for pull requests if [[ ! $deps && ! $PHP = ${MIN_PHP%.*} && ! $PHP = hhvm* && $TRAVIS_PULL_REQUEST != false ]]; then @@ -149,6 +153,10 @@ install: export COMPOSER_ROOT_VERSION=$SYMFONY_VERSION.x-dev if [[ ! $skip && $deps ]]; then mv composer.json.phpunit composer.json; fi + if [[ ! $skip && $PHP = 7.* ]]; then + ([[ $deps ]] && cd src/Symfony/Component/HttpFoundation; composer require --dev --no-update mongodb/mongodb) + fi + - if [[ ! $skip ]]; then $COMPOSER_UP; fi - if [[ ! $skip ]]; then ./phpunit install; fi - | diff --git a/CHANGELOG-3.3.md b/CHANGELOG-3.3.md index 39137503cd411..17b9ed1ed3073 100644 --- a/CHANGELOG-3.3.md +++ b/CHANGELOG-3.3.md @@ -7,6 +7,45 @@ 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 (2017-05-29) + + * bug #22940 [Config] Fallback to regular import when glob fails (nicolas-grekas) + * bug #22847 [Console] ChoiceQuestion must have choices (ro0NL) + * bug #22900 [FrameworkBundle][Console] Fix the override of a command registered by the kernel (aaa2000) + * bug #22930 Revert "bug #22925 [PhpUnitBridge] Adjust PHPUnit class_alias check (nicolas-grekas) + * bug #22910 [Filesystem] improve error handling in lock() (xabbuh) + * bug #22924 [Cache] Dont use pipelining with RedisCluster (nicolas-grekas) + * bug #22928 [WebProfilerBundle] Fixed options stub values display in form profiler (HeahDude) + * feature #22838 Make the simple exception pages match the new style (javiereguiluz) + * bug #22925 [PhpUnitBridge] Adjust PHPUnit class_alias check to also check for namespaced class (GawainLynch) + * bug #22718 [Console] Fixed different behaviour of key and value user inputs in multiple choice question (borNfreee) + * bug #22921 [FrameworkBundle] Only override getProjectDir if it exists in the kernel (aschempp) + * feature #22905 [FrameworkBundle][Validator] Move the PSR-11 factory to the component (ogizanagi) + * bug #22728 [HttpKernel] Fix kernel.project_dir extensibility (chalasr) + * bug #22829 [Yaml] fix colon without space deprecation (xabbuh) + * bug #22901 Fix missing abstract key in XmlDumper (weaverryan) + * bug #22912 [DI] Avoid private call to Container::has() (ro0NL) + * feature #22904 [HttpFoundation] Add Request::HEADER_X_FORWARDED_AWS_ELB const (nicolas-grekas) + * bug #22878 [Yaml] parse PHP constants in mapping keys (xabbuh) + * bug #22873 [HttpKernel] don't call getTrustedHeaderName() if possible (xabbuh) + * feature #22892 [ProxyManager] Add FC layer (nicolas-grekas) + * bug #22866 [DI] Check for privates before shared services (ro0NL) + * feature #22884 [DI] Add missing deprecation on Extension::getClassesToCompile (nicolas-grekas) + * bug #22874 [WebProfilerBundle] Fix sub-requests display in time profiler panel (nicolas-grekas) + * bug #22853 [Yaml] fix multiline block handling (xabbuh) + * bug #22872 [FrameworkBundle] Handle project dir in cache:clear command (nicolas-grekas) + * feature #22808 [FrameworkBundle][Validator] Deprecate passing validator instances/aliases over using the service locator (ogizanagi) + * bug #22857 [DI] Fix autowire error for inlined services (weaverryan) + * bug #22858 [SecurityBundle] Prevent auto-registration of UserPasswordEncoderCommand (chalasr) + * bug #22859 [Profiler][VarDumper] Fix searchbar css when in toolbar (ogizanagi) + * bug #22614 [Process] Fixed escaping arguments on Windows when inheritEnvironmentVariables is set to false (maryo) + * bug #22817 [PhpUnitBridge] optional error handler arguments (xabbuh) + * bug #22781 [DI][Serializer] Fix missing de(normalizer|coder) autoconfig (ogizanagi) + * bug #22790 [DependencyInjection] Fix dumping of RewindableGenerator with empty IteratorArgument (meyerbaptiste) + * bug #22787 [MonologBridge] Fix the Monlog ServerLogHandler from Hanging on Windows (ChadSikorra) + * bug #22768 Use 0.0.0.0 as the server log command host default. (ChadSikorra) + * bug #22752 Improved how profiler errors are displayed on small screens (javiereguiluz) + * 3.3.0-RC1 (2017-05-17) * bug #22715 [FrameworkBundle] remove Security deps from the require section (xabbuh) diff --git a/UPGRADE-3.2.md b/UPGRADE-3.2.md index e151275472755..8a14f2d4b6281 100644 --- a/UPGRADE-3.2.md +++ b/UPGRADE-3.2.md @@ -64,6 +64,15 @@ DependencyInjection * Calling `get()` on a `ContainerBuilder` instance before compiling the container is deprecated and will throw an exception in Symfony 4.0. + * Setting or unsetting a private service with the `Container::set()` method is + deprecated. Only public services can be set or unset in Symfony 4.0. + + * Checking the existence of a private service with the `Container::has()` + method is deprecated and will return `false` in Symfony 4.0. + + * Requesting a private service with the `Container::get()` method is deprecated + and will no longer be supported in Symfony 4.0. + ExpressionLanguage ------------------- diff --git a/UPGRADE-3.3.md b/UPGRADE-3.3.md index 44044e73582fc..05f44e549e11b 100644 --- a/UPGRADE-3.3.md +++ b/UPGRADE-3.3.md @@ -82,6 +82,8 @@ 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] The `NullDumper` class has been made final + * [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. @@ -201,11 +203,6 @@ FrameworkBundle 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 @@ -244,6 +241,10 @@ FrameworkBundle class has been deprecated and will be removed in 4.0. Use the `Symfony\Component\Workflow\DependencyInjection\ValidateWorkflowsPass` class instead. + * The `Symfony\Bundle\FrameworkBundle\Validator\ConstraintValidatorFactory` + class has been deprecated and will be removed in 4.0. + Use `Symfony\Component\Validator\ContainerConstraintValidatorFactory` instead. + HttpFoundation -------------- @@ -262,7 +263,7 @@ HttpKernel * 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 `Extension::addClassesToCompile()` and `Extension::getClassesToCompile()` methods have 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. @@ -297,13 +298,18 @@ Process * Extending `Process::run()`, `Process::mustRun()` and `Process::restart()` is deprecated and won't be supported in 4.0. +ProxyManager +------------ + + * [BC BREAK] The `ProxyDumper` class has been made final + 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. + * The `LogoutUrlGenerator::registerListener()` method will expect a 6th `string $context = null` argument in 4.0. Define the argument when overriding this method. * The `AccessDecisionManager::setVoters()` method has been deprecated. Pass diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md index 6424371880704..6c10190c7c502 100644 --- a/UPGRADE-4.0.md +++ b/UPGRADE-4.0.md @@ -294,11 +294,6 @@ FrameworkBundle 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 @@ -331,6 +326,9 @@ FrameworkBundle * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ValidateWorkflowsPass` class has been removed. Use the `Symfony\Component\Workflow\DependencyInjection\ValidateWorkflowsPass` class instead. + + * The `Symfony\Bundle\FrameworkBundle\Validator\ConstraintValidatorFactory` class has been removed. + Use `Symfony\Component\Validator\ContainerConstraintValidatorFactory` instead. HttpFoundation -------------- @@ -372,7 +370,7 @@ HttpKernel * Removed the `Kernel::getRootDir()` method. Use the `Kernel::getProjectDir()` method instead. - * The `Extension::addClassesToCompile()` method has been removed. + * The `Extension::addClassesToCompile()` and `Extension::getClassesToCompile()` methods have 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 @@ -420,13 +418,18 @@ Process * Extending `Process::run()`, `Process::mustRun()` and `Process::restart()` is not supported anymore. +ProxyManager +------------ + + * The `ProxyDumper` class has been made final + 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 `LogoutUrlGenerator::registerListener()` method expects a 6th `string $context = null` argument. * The `AccessDecisionManager::setVoters()` method has been removed. Pass the voters to the constructor instead. diff --git a/appveyor.yml b/appveyor.yml index f6563e730a32e..a1ba60ac1b02d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,7 +19,7 @@ install: - appveyor DownloadFile https://raw.githubusercontent.com/symfony/binary-utils/master/cacert.pem - appveyor DownloadFile https://github.com/symfony/binary-utils/releases/download/v0.1/php-5.5.9-nts-Win32-VC11-x86.zip - 7z x php-5.5.9-nts-Win32-VC11-x86.zip -y >nul - - appveyor DownloadFile https://github.com/symfony/binary-utils/releases/download/v0.1/php-7.1.3-Win32-VC14-x64.zip + - appveyor DownloadFile https://github.com/symfony/binary-utils/releases/download/v0.1/php-7.1.3-Win32-VC14-x86.zip - cd ext - appveyor DownloadFile https://github.com/symfony/binary-utils/releases/download/v0.1/php_apcu-4.0.10-5.5-nts-vc11-x86.zip - 7z x php_apcu-4.0.10-5.5-nts-vc11-x86.zip -y >nul @@ -56,7 +56,7 @@ install: test_script: - SET X=0 - - cd c:\php && 7z x php-7.1.3-Win32-VC14-x64.zip -y >nul && copy /Y php.ini-min php.ini + - cd c:\php && 7z x php-7.1.3-Win32-VC14-x86.zip -y >nul && copy /Y php.ini-min php.ini - cd c:\projects\symfony - php phpunit src\Symfony --exclude-group benchmark,intl-data || SET X=!errorlevel! - cd c:\php && 7z x php-5.5.9-nts-Win32-VC11-x86.zip -y >nul && copy /Y php.ini-min php.ini diff --git a/phpunit b/phpunit index 466f8fbbc2356..559660b1df25f 100755 --- a/phpunit +++ b/phpunit @@ -1,7 +1,7 @@ #!/usr/bin/env php formatRecord($record); - if (!fwrite($this->socket, $recordFormatted)) { - fclose($this->socket); + if (-1 === stream_socket_sendto($this->socket, $recordFormatted)) { + stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR); // Let's retry: the persistent connection might just be stale if ($this->socket = $this->createSocket()) { - fwrite($this->socket, $recordFormatted); + stream_socket_sendto($this->socket, $recordFormatted); } } } finally { diff --git a/src/Symfony/Bridge/PhpUnit/CHANGELOG.md b/src/Symfony/Bridge/PhpUnit/CHANGELOG.md new file mode 100644 index 0000000000000..1cb07da782b6d --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/CHANGELOG.md @@ -0,0 +1,16 @@ +CHANGELOG +========= + +3.3.0 +----- + + * using the `testLegacy` prefix in method names to mark a test as legacy is + deprecated, use the `@group legacy` notation instead + * using the `Legacy` prefix in class names to mark a test as legacy is deprecated, + use the `@group legacy` notation instead + +3.1.0 +----- + + * passing a numerically indexed array to the constructor of the `SymfonyTestsListenerTrait` + is deprecated, pass an array of namespaces indexed by the mocked feature instead diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php index 4279995ec91ca..0f54a52bb8cab 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php @@ -97,7 +97,7 @@ public static function register($mode = 0) 'other' => array(), 'remaining vendor' => array(), ); - $deprecationHandler = function ($type, $msg, $file, $line, $context) use (&$deprecations, $getMode, $UtilPrefix, $inVendors) { + $deprecationHandler = function ($type, $msg, $file, $line, $context = array()) use (&$deprecations, $getMode, $UtilPrefix, $inVendors) { $mode = $getMode(); if ((E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) || DeprecationErrorHandler::MODE_DISABLED === $mode) { $ErrorHandler = $UtilPrefix.'ErrorHandler'; diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php index f8814c3c844a7..173a0fa82fd93 100644 --- a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php +++ b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php @@ -16,7 +16,6 @@ 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; 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 index 7bbda8775d6d5..7e9c6f8ed7682 100644 --- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_vendor.phpt +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_vendor.phpt @@ -17,6 +17,8 @@ require PHPUNIT_COMPOSER_INSTALL; require_once __DIR__.'/../../bootstrap.php'; require __DIR__.'/fake_vendor/autoload.php'; require __DIR__.'/fake_vendor/acme/lib/deprecation_riddled.php'; + +?> --EXPECTF-- Unsilenced deprecation notices (2) diff --git a/src/Symfony/Bridge/ProxyManager/CHANGELOG.md b/src/Symfony/Bridge/ProxyManager/CHANGELOG.md index 1f8f60c48bfed..56c8b20e28272 100644 --- a/src/Symfony/Bridge/ProxyManager/CHANGELOG.md +++ b/src/Symfony/Bridge/ProxyManager/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +3.3.0 +----- + + * [BC BREAK] The `ProxyDumper` class is now final + 2.3.0 ----- diff --git a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php index 447549b970442..b91bfeb922cf8 100644 --- a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php +++ b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php @@ -22,6 +22,8 @@ * Generates dumped PHP code of proxies via reflection. * * @author Marco Pivetta + * + * @final since version 3.3 */ class ProxyDumper implements DumperInterface { @@ -63,7 +65,7 @@ public function isProxyCandidate(Definition $definition) /** * {@inheritdoc} */ - public function getProxyFactoryCode(Definition $definition, $id) + public function getProxyFactoryCode(Definition $definition, $id, $methodName = null) { $instantiation = 'return'; 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 82d52706d9a1a..51e13a843033f 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 -%} @@ -259,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 -%} diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index df80163243c1c..d25f4547d3123 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -31,7 +31,6 @@ CHANGELOG * 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 @@ -47,8 +46,10 @@ CHANGELOG `Symfony\Component\Validator\DependencyInjection\AddValidatorInitializersPass` instead * Deprecated `AddConstraintValidatorsPass`, use `Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass` instead - * Deprecated `ValidateWorkflowsPass`, use + * Deprecated `ValidateWorkflowsPass`, use `Symfony\Component\Workflow\DependencyInjection\ValidateWorkflowsPass` instead + * Deprecated `ConstraintValidatorFactory`, use + `Symfony\Component\Validator\ContainerConstraintValidatorFactory` instead. 3.2.0 ----- diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php index 76705f08d5e7d..6db6da85e065a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php @@ -46,7 +46,6 @@ protected function execute(InputInterface $input, OutputInterface $output) /** @var $kernel KernelInterface */ $kernel = $this->getContainer()->get('kernel'); - $baseDir = realpath($kernel->getRootDir().DIRECTORY_SEPARATOR.'..'); $io->table(array(), array( array('Symfony'), @@ -62,9 +61,9 @@ protected function execute(InputInterface $input, OutputInterface $output) 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()).')'), + array('Root directory', self::formatPath($kernel->getRootDir(), $kernel->getProjectDir())), + array('Cache directory', self::formatPath($kernel->getCacheDir(), $kernel->getProjectDir()).' ('.self::formatFileSize($kernel->getCacheDir()).')'), + array('Log directory', self::formatPath($kernel->getLogDir(), $kernel->getProjectDir()).' ('.self::formatFileSize($kernel->getLogDir()).')'), new TableSeparator(), array('PHP'), new TableSeparator(), diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php index feb966d850939..136b1c21fae98 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php @@ -82,9 +82,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $targetArg = rtrim($input->getArgument('target'), '/'); if (!is_dir($targetArg)) { - $appRoot = $this->getContainer()->getParameter('kernel.root_dir').'/..'; - - $targetArg = $appRoot.'/'.$targetArg; + $targetArg = $this->getContainer()->getParameter('kernel.project_dir').'/'.$targetArg; if (!is_dir($targetArg)) { throw new \InvalidArgumentException(sprintf('The target directory "%s" does not exist.', $input->getArgument('target'))); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php index 49284270e6e34..e13d5b33c7d8e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php @@ -199,6 +199,7 @@ protected function warmup($warmupDir, $realCacheDir, $enableOptionalWarmers = tr */ protected function getTempKernel(KernelInterface $parent, $namespace, $parentClass, $warmupDir) { + $projectDir = ''; $cacheDir = var_export($warmupDir, true); $rootDir = var_export(realpath($parent->getRootDir()), true); $logDir = var_export(realpath($parent->getLogDir()), true); @@ -207,6 +208,18 @@ protected function getTempKernel(KernelInterface $parent, $namespace, $parentCla $class = substr($parentClass, 0, -1).'_'; // the temp container class must be changed too $containerClass = var_export(substr(get_class($parent->getContainer()), 0, -1).'_', true); + + if (method_exists($parent, 'getProjectDir')) { + $projectDir = var_export(realpath($parent->getProjectDir()), true); + $projectDir = <<%s, env: %s, debug: %s)', $this->kernel->getName(), $this->kernel->getEnvironment(), $this->kernel->isDebug() ? 'true' : 'false'); } + public function add(Command $command) + { + $this->registerCommands(); + + return parent::add($command); + } + protected function registerCommands() { if ($this->commandsRegistered) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php index c081dfc6e96c6..268a7818979cb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php @@ -258,10 +258,6 @@ protected function resolveServiceDefinition(ContainerBuilder $builder, $serviceI return $builder->getAlias($serviceId); } - if ('service_container' === $serviceId) { - return $builder; - } - // the service has been injected in some special way, just return the service return $builder->get($serviceId); } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index c65c1f8e740be..e72a02d24480e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -47,11 +47,13 @@ use Symfony\Component\PropertyInfo\PropertyListExtractorInterface; use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; use Symfony\Component\Serializer\Encoder\CsvEncoder; +use Symfony\Component\Serializer\Encoder\DecoderInterface; 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\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\JsonSerializableNormalizer; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Validator\ConstraintValidatorInterface; @@ -276,8 +278,12 @@ public function load(array $configs, ContainerBuilder $container) ->addTag('property_info.access_extractor'); $container->registerForAutoconfiguration(EncoderInterface::class) ->addTag('serializer.encoder'); + $container->registerForAutoconfiguration(DecoderInterface::class) + ->addTag('serializer.encoder'); $container->registerForAutoconfiguration(NormalizerInterface::class) ->addTag('serializer.normalizer'); + $container->registerForAutoconfiguration(DenormalizerInterface::class) + ->addTag('serializer.normalizer'); $container->registerForAutoconfiguration(ConstraintValidatorInterface::class) ->addTag('validator.constraint_validator'); $container->registerForAutoconfiguration(ObjectInitializerInterface::class) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml index f895eb4cc0bdf..5f505e859c82b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml @@ -59,7 +59,7 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php index fc60fd3bdd71c..25511142c9b54 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php @@ -115,6 +115,21 @@ public function testBundleCommandsHaveRightContainer() $tester->run(array('command' => 'foo')); } + public function testBundleCommandCanOverriddeAPreExistingCommandWithTheSameName() + { + $command = new Command('example'); + + $bundle = $this->createBundleMock(array($command)); + + $kernel = $this->getKernel(array($bundle)); + + $application = new Application($kernel); + $newCommand = new Command('example'); + $application->add($newCommand); + + $this->assertSame($newCommand, $application->get('example')); + } + private function getKernel(array $bundles, $useDispatcher = false) { $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Validator/ConstraintValidatorFactoryTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Validator/ConstraintValidatorFactoryTest.php index 6cf9574ece96b..f619584023a77 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Validator/ConstraintValidatorFactoryTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Validator/ConstraintValidatorFactoryTest.php @@ -14,8 +14,14 @@ use PHPUnit\Framework\TestCase; use Symfony\Bundle\FrameworkBundle\Validator\ConstraintValidatorFactory; use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\Blank as BlankConstraint; +use Symfony\Component\Validator\ConstraintValidator; +/** + * @group legacy + */ class ConstraintValidatorFactoryTest extends TestCase { public function testGetInstanceCreatesValidator() @@ -24,7 +30,7 @@ public function testGetInstanceCreatesValidator() $constraint = $this->getMockBuilder('Symfony\\Component\\Validator\\Constraint')->getMock(); $constraint - ->expects($this->once()) + ->expects($this->exactly(2)) ->method('validatedBy') ->will($this->returnValue($class)); @@ -41,6 +47,34 @@ public function testGetInstanceReturnsExistingValidator() } public function testGetInstanceReturnsService() + { + $service = 'validator_constraint_service'; + $validator = $this->getMockForAbstractClass(ConstraintValidator::class); + + // mock ContainerBuilder b/c it implements TaggedContainerInterface + $container = $this->getMockBuilder(ContainerBuilder::class)->setMethods(array('get', 'has'))->getMock(); + $container + ->expects($this->once()) + ->method('get') + ->with($service) + ->willReturn($validator); + $container + ->expects($this->once()) + ->method('has') + ->with($service) + ->willReturn(true); + + $constraint = $this->getMockBuilder(Constraint::class)->getMock(); + $constraint + ->expects($this->exactly(2)) + ->method('validatedBy') + ->will($this->returnValue($service)); + + $factory = new ConstraintValidatorFactory($container); + $this->assertSame($validator, $factory->getInstance($constraint)); + } + + public function testGetInstanceReturnsServiceWithAlias() { $service = 'validator_constraint_service'; $alias = 'validator_constraint_alias'; @@ -71,7 +105,7 @@ public function testGetInstanceInvalidValidatorClass() { $constraint = $this->getMockBuilder('Symfony\\Component\\Validator\\Constraint')->getMock(); $constraint - ->expects($this->once()) + ->expects($this->exactly(2)) ->method('validatedBy') ->will($this->returnValue('Fully\\Qualified\\ConstraintValidator\\Class\\Name')); diff --git a/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php b/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php index 6bcbaa8e97416..2b83e14b10e66 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php +++ b/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php @@ -48,10 +48,11 @@ class Translator extends BaseTranslator implements WarmableInterface * * debug: Whether to enable debugging or not (false by default) * * resource_files: List of translation resources available grouped by locale. * - * @param ContainerInterface $container A ContainerInterface instance - * @param MessageSelector $selector The message selector for pluralization - * @param array $loaderIds An array of loader Ids - * @param array $options An array of options + * @param ContainerInterface $container A ContainerInterface instance + * @param MessageSelector $selector The message selector for pluralization + * @param string $defaultLocale + * @param array $loaderIds An array of loader Ids + * @param array $options An array of options * * @throws InvalidArgumentException */ diff --git a/src/Symfony/Bundle/FrameworkBundle/Validator/ConstraintValidatorFactory.php b/src/Symfony/Bundle/FrameworkBundle/Validator/ConstraintValidatorFactory.php index aba02e0944e22..1a9020ad687a3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Validator/ConstraintValidatorFactory.php +++ b/src/Symfony/Bundle/FrameworkBundle/Validator/ConstraintValidatorFactory.php @@ -13,10 +13,12 @@ use Psr\Container\ContainerInterface; use Symfony\Component\Validator\Constraint; -use Symfony\Component\Validator\ConstraintValidatorFactoryInterface; use Symfony\Component\Validator\ConstraintValidatorInterface; -use Symfony\Component\Validator\Exception\ValidatorException; +use Symfony\Component\Validator\ContainerConstraintValidatorFactory; use Symfony\Component\Validator\Exception\UnexpectedTypeException; +use Symfony\Component\Validator\Exception\ValidatorException; + +@trigger_error(sprintf('The %s class is deprecated since version 3.3 and will be removed in 4.0. Use %s instead.', ConstraintValidatorFactory::class, ContainerConstraintValidatorFactory::class), E_USER_DEPRECATED); /** * Uses a service container to create constraint validators. @@ -38,23 +40,19 @@ * * @author Kris Wallsmith * - * @final since version 3.3 + * @deprecated since version 3.3 */ -class ConstraintValidatorFactory implements ConstraintValidatorFactoryInterface +class ConstraintValidatorFactory extends ContainerConstraintValidatorFactory { protected $container; protected $validators; - /** - * Constructor. - * - * @param ContainerInterface $container The service container - * @param array $validators An array of validators - */ public function __construct(ContainerInterface $container, array $validators = array()) { - $this->container = $container; + parent::__construct($container); + $this->validators = $validators; + $this->container = $container; } /** @@ -72,16 +70,10 @@ public function getInstance(Constraint $constraint) $name = $constraint->validatedBy(); if (!isset($this->validators[$name])) { - if ($this->container->has($name)) { - $this->validators[$name] = $this->container->get($name); - } else { - if (!class_exists($name)) { - throw new ValidatorException(sprintf('Constraint validator "%s" does not exist or it is not enabled. Check the "validatedBy" method in your constraint class "%s".', $name, get_class($constraint))); - } + return parent::getInstance($constraint); + } - $this->validators[$name] = new $name(); - } - } elseif (is_string($this->validators[$name])) { + if (is_string($this->validators[$name])) { $this->validators[$name] = $this->container->get($this->validators[$name]); } diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 75725c32b90b1..70b922c1fcc1f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -49,7 +49,7 @@ "symfony/serializer": "~3.3", "symfony/translation": "~3.2", "symfony/templating": "~2.8|~3.0", - "symfony/validator": "~3.3", + "symfony/validator": "~3.3-rc2", "symfony/workflow": "~3.3", "symfony/yaml": "~3.2", "symfony/property-info": "~3.3", @@ -69,7 +69,7 @@ "symfony/property-info": "<3.3", "symfony/serializer": "<3.3", "symfony/translation": "<3.2", - "symfony/validator": "<3.3", + "symfony/validator": "<3.3-rc2", "symfony/workflow": "<3.3" }, "suggest": { diff --git a/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md b/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md index 9691e5af03c16..4185d409fb445 100644 --- a/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md @@ -10,6 +10,7 @@ CHANGELOG `ContainerAwareInterface` interface for this command. * Deprecated the `FirewallMap::$map` and `$container` properties. * [BC BREAK] Keys of the `users` node for `in_memory` user provider are no longer normalized. + * deprecated `FirewallContext::getListeners()` 3.2.0 ----- diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index a54244d67c663..4191506f1ece3 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -98,11 +98,11 @@ public function load(array $configs, ContainerBuilder $container) if ($config['encoders']) { $this->createEncoders($config['encoders'], $container); + } - if (class_exists(Application::class)) { - $loader->load('console.xml'); - $container->getDefinition('security.console.user_password_encoder_command')->replaceArgument(1, array_keys($config['encoders'])); - } + if (class_exists(Application::class)) { + $loader->load('console.xml'); + $container->getDefinition('security.console.user_password_encoder_command')->replaceArgument(1, array_keys($config['encoders'])); } // load ACL diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php index bfa0f1b877b28..6ef0e305ec4f6 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php @@ -345,6 +345,11 @@ public function testUserCheckerConfigWithNoCheckers() $this->assertEquals('security.user_checker', $this->getContainer('container1')->getAlias('security.user_checker.secure')); } + public function testUserPasswordEncoderCommandIsRegistered() + { + $this->assertTrue($this->getContainer('remember_me_options')->has('security.console.user_password_encoder_command')); + } + protected function getContainer($file) { $file = $file.'.'.$this->getFileExtension(); diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig index 90b268fd4fb76..2f8b83049d158 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig @@ -624,7 +624,10 @@ {{ option }} {{ profiler_dump(value) }} - {% if data.resolved_options[option] == value %} + {# values can be stubs #} + {% set option_value = value.value|default(value) %} + {% set resolved_option_value = data.resolved_options[option].value|default(data.resolved_options[option]) %} + {% if resolved_option_value == option_value %} same as passed value {% else %} {{ profiler_dump(data.resolved_options[option]) }} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig index ea73c8bde717e..28e3a3c8385af 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig @@ -107,8 +107,8 @@ {% if profile.parent %} -

- Sub-Request {{ profile.getcollector('request').requestattributes.get('_controller') }} +

+ Sub-Request {{ profiler_dump(profile.getcollector('request').requestattributes.get('_controller')) }} {{ collector.events.__section__.duration }} ms Return to parent request diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig index b90d585e59691..5b6647df8866a 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig @@ -992,6 +992,18 @@ table.logs .metadata { display: block; } + #sidebar:not(:hover):not(.expanded) .label .count { + border-radius: 50%; + border: 1px solid #eee; + height: 8px; + min-width: 0; + padding: 0; + right: 4px; + text-indent: -9999px; + top: 50%; + width: 8px; + } + .visible-small { display: inherit; } diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig index 4092f7547aed6..3a8f6e0fbadc9 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig @@ -382,6 +382,9 @@ .sf-toolbar-block-dump pre.sf-dump:last-child { margin-bottom: 0; } +.sf-toolbar-block-dump pre.sf-dump .sf-dump-search-wrapper { + margin-bottom: 5px; +} .sf-toolbar-block-dump pre.sf-dump span.sf-dump-search-count { color: #333; font-size: 12px; diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php index b5a9b53e5dbdc..28dbb976d3fbb 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php @@ -27,10 +27,13 @@ class WebProfilerExtensionTest extends TestCase */ private $container; - public static function assertSaneContainer(Container $container, $message = '') + public static function assertSaneContainer(Container $container, $message = '', $knownPrivates = array()) { $errors = array(); foreach ($container->getServiceIds() as $id) { + if (in_array($id, $knownPrivates, true)) { // to be removed in 4.0 + continue; + } try { $container->get($id); } catch (\Exception $e) { @@ -98,7 +101,7 @@ public function testToolbarConfig($toolbarEnabled, $interceptRedirects, $listene $this->assertSame($listenerInjected, $this->container->has('web_profiler.debug_toolbar')); - $this->assertSaneContainer($this->getDumpedContainer()); + $this->assertSaneContainer($this->getDumpedContainer(), '', array('web_profiler.csp.handler')); if ($listenerInjected) { $this->assertSame($listenerEnabled, $this->container->get('web_profiler.debug_toolbar')->isEnabled()); diff --git a/src/Symfony/Bundle/WebServerBundle/Command/ServerCommand.php b/src/Symfony/Bundle/WebServerBundle/Command/ServerCommand.php index 40a0da6e08bce..1df81a68a173b 100644 --- a/src/Symfony/Bundle/WebServerBundle/Command/ServerCommand.php +++ b/src/Symfony/Bundle/WebServerBundle/Command/ServerCommand.php @@ -17,6 +17,8 @@ * Base methods for commands related to a local web server. * * @author Christian Flothmann + * + * @internal */ abstract class ServerCommand extends Command { diff --git a/src/Symfony/Bundle/WebServerBundle/Command/ServerLogCommand.php b/src/Symfony/Bundle/WebServerBundle/Command/ServerLogCommand.php index 50d53202277b2..eed32c5a2055f 100644 --- a/src/Symfony/Bundle/WebServerBundle/Command/ServerLogCommand.php +++ b/src/Symfony/Bundle/WebServerBundle/Command/ServerLogCommand.php @@ -47,7 +47,7 @@ protected function configure() } $this - ->addOption('host', null, InputOption::VALUE_REQUIRED, 'The server host', '0:9911') + ->addOption('host', null, InputOption::VALUE_REQUIRED, 'The server host', '0.0.0.0:9911') ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The line format', ConsoleFormatter::SIMPLE_FORMAT) ->addOption('date-format', null, InputOption::VALUE_REQUIRED, 'The date format', ConsoleFormatter::SIMPLE_DATE) ->addOption('filter', null, InputOption::VALUE_REQUIRED, 'An expression to filter log. Example: "level > 200 or channel in [\'app\', \'doctrine\']"') diff --git a/src/Symfony/Bundle/WebServerBundle/Resources/config/webserver.xml b/src/Symfony/Bundle/WebServerBundle/Resources/config/webserver.xml index c45fff0a23d4a..2815c6d2cfbf5 100644 --- a/src/Symfony/Bundle/WebServerBundle/Resources/config/webserver.xml +++ b/src/Symfony/Bundle/WebServerBundle/Resources/config/webserver.xml @@ -8,13 +8,13 @@ - %kernel.root_dir%/../web + %kernel.project_dir%/web %kernel.environment% - %kernel.root_dir%/../web + %kernel.project_dir%/web %kernel.environment% diff --git a/src/Symfony/Bundle/WebServerBundle/composer.json b/src/Symfony/Bundle/WebServerBundle/composer.json index c87dab356e2d4..ce8a60d136eb0 100644 --- a/src/Symfony/Bundle/WebServerBundle/composer.json +++ b/src/Symfony/Bundle/WebServerBundle/composer.json @@ -18,7 +18,7 @@ "require": { "php": ">=5.5.9", "symfony/console": "~2.8.8|~3.0.8|~3.1.2|~3.2", - "symfony/http-kernel": "~2.8|~3.0", + "symfony/http-kernel": "~3.3", "symfony/process": "~2.8|~3.0" }, "autoload": { diff --git a/src/Symfony/Component/Cache/README.md b/src/Symfony/Component/Cache/README.md index 604fb1d5b410c..c4ab7520f451e 100644 --- a/src/Symfony/Component/Cache/README.md +++ b/src/Symfony/Component/Cache/README.md @@ -7,3 +7,12 @@ low overhead so that caching is fastest. It ships with a few caching adapters for the most widespread and suited to caching backends. It also provides a `doctrine/cache` proxy adapter to cover more advanced caching needs and a proxy adapter for greater interoperability between PSR-6 implementations. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/cache.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/src/Symfony/Component/Cache/Traits/RedisTrait.php b/src/Symfony/Component/Cache/Traits/RedisTrait.php index 0ef6d50e35582..8877b0911a76b 100644 --- a/src/Symfony/Component/Cache/Traits/RedisTrait.php +++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php @@ -303,6 +303,14 @@ private function pipeline(\Closure $generator) foreach ($results as $k => list($h, $c)) { $results[$k] = $connections[$h][$c]; } + } elseif ($this->redis instanceof \RedisCluster) { + // phpredis doesn't support pipelining with RedisCluster + // see https://github.com/phpredis/phpredis/blob/develop/cluster.markdown#pipelining + $results = array(); + foreach ($generator() as $command => $args) { + $results[] = call_user_func_array(array($this->redis, $command), $args); + $ids[] = $args[0]; + } } else { $this->redis->multi(\Redis::PIPELINE); foreach ($generator() as $command => $args) { diff --git a/src/Symfony/Component/Cache/phpunit.xml.dist b/src/Symfony/Component/Cache/phpunit.xml.dist index 13bff46d01ed0..6b5c79331d14f 100644 --- a/src/Symfony/Component/Cache/phpunit.xml.dist +++ b/src/Symfony/Component/Cache/phpunit.xml.dist @@ -34,10 +34,14 @@ - Cache\IntegrationTests - Doctrine\Common\Cache - Symfony\Component\Cache - Symfony\Component\Cache\Traits + + + Cache\IntegrationTests + Doctrine\Common\Cache + Symfony\Component\Cache + Symfony\Component\Cache\Traits + + diff --git a/src/Symfony/Component/Config/Loader/FileLoader.php b/src/Symfony/Component/Config/Loader/FileLoader.php index 6119e5a5b1118..d3fe0eacfc0d9 100644 --- a/src/Symfony/Component/Config/Loader/FileLoader.php +++ b/src/Symfony/Component/Config/Loader/FileLoader.php @@ -83,18 +83,17 @@ public function getLocator() */ public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null) { - $ret = array(); - $ct = 0; - if (!is_string($resource) || false === strpbrk($resource, '*?{[')) { - $ret[] = $this->doImport($resource, $type, $ignoreErrors, $sourceResource); - } else { - foreach ($this->glob($resource, false, $_, $ignoreErrors) as $path => $info) { - ++$ct; + if (is_string($resource) && false !== strpbrk($resource, '*?{[')) { + $ret = array(); + foreach ($this->glob($resource, false, $_, true) as $path => $info) { $ret[] = $this->doImport($path, $type, $ignoreErrors, $sourceResource); } + if ($ret) { + return count($ret) > 1 ? $ret : $ret[0]; + } } - return $ct > 1 ? $ret : (isset($ret[0]) ? $ret[0] : null); + return $this->doImport($resource, $type, $ignoreErrors, $sourceResource); } /** diff --git a/src/Symfony/Component/Config/Tests/Loader/FileLoaderTest.php b/src/Symfony/Component/Config/Tests/Loader/FileLoaderTest.php index e1d23e45fa6d1..59b625525bff1 100644 --- a/src/Symfony/Component/Config/Tests/Loader/FileLoaderTest.php +++ b/src/Symfony/Component/Config/Tests/Loader/FileLoaderTest.php @@ -66,6 +66,14 @@ public function testImportWithFileLocatorDelegation() $this->assertInstanceOf('Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException', $e, '->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading'); } } + + public function testImportWithGlobLikeResource() + { + $locatorMock = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')->getMock(); + $loader = new TestFileLoader($locatorMock); + + $this->assertSame('[foo]', $loader->import('[foo]')); + } } class TestFileLoader extends FileLoader diff --git a/src/Symfony/Component/Console/Question/ChoiceQuestion.php b/src/Symfony/Component/Console/Question/ChoiceQuestion.php index 522a81bb146d5..5815e2b04cb49 100644 --- a/src/Symfony/Component/Console/Question/ChoiceQuestion.php +++ b/src/Symfony/Component/Console/Question/ChoiceQuestion.php @@ -34,6 +34,10 @@ class ChoiceQuestion extends Question */ public function __construct($question, array $choices, $default = null) { + if (!$choices) { + throw new \LogicException('Choice question must have at least 1 choice available.'); + } + parent::__construct($question, $default); $this->choices = $choices; @@ -137,7 +141,7 @@ private function getDefaultValidator() if ($multiselect) { // Check for a separated comma values - if (!preg_match('/^[a-zA-Z0-9_-]+(?:,[a-zA-Z0-9_-]+)*$/', $selectedChoices, $matches)) { + if (!preg_match('/^[^,]+(?:,[^,]+)*$/', $selectedChoices, $matches)) { throw new InvalidArgumentException(sprintf($errorMessage, $selected)); } $selectedChoices = explode(',', $selectedChoices); diff --git a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php index 700bfe240d253..3d19e800b4580 100644 --- a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php @@ -267,6 +267,37 @@ public function simpleAnswerProvider() ); } + /** + * @dataProvider specialCharacterInMultipleChoice + */ + public function testSpecialCharacterChoiceFromMultipleChoiceList($providedAnswer, $expectedValue) + { + $possibleChoices = array( + '.', + 'src', + ); + + $dialog = new QuestionHelper(); + $inputStream = $this->getInputStream($providedAnswer."\n"); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new ChoiceQuestion('Please select the directory', $possibleChoices); + $question->setMaxAttempts(1); + $question->setMultiselect(true); + $answer = $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question); + + $this->assertSame($expectedValue, $answer); + } + + public function specialCharacterInMultipleChoice() + { + return array( + array('.', array('.')), + array('., src', array('.', 'src')), + ); + } + /** * @dataProvider mixedKeysChoiceListAnswerProvider */ @@ -772,6 +803,15 @@ public function testAskThrowsExceptionOnMissingInputWithValidator() $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream('')), $this->createOutputInterface(), $question); } + /** + * @expectedException \LogicException + * @expectedExceptionMessage Choice question must have at least 1 choice available. + */ + public function testEmptyChoices() + { + new ChoiceQuestion('Question', array(), 'irrelevant'); + } + protected function getInputStream($input) { $stream = fopen('php://memory', 'r+', false); diff --git a/src/Symfony/Component/Debug/ExceptionHandler.php b/src/Symfony/Component/Debug/ExceptionHandler.php index f2ae4b102e13d..1fe60d9f31e69 100644 --- a/src/Symfony/Component/Debug/ExceptionHandler.php +++ b/src/Symfony/Component/Debug/ExceptionHandler.php @@ -220,28 +220,30 @@ public function getContent(FlattenException $exception) $class = $this->formatClass($e['class']); $message = nl2br($this->escapeHtml($e['message'])); $content .= sprintf(<<<'EOF' -

- %d/%d - %s%s: - %s -

-
-
    - +
    + + + EOF - , $ind, $total, $class, $this->formatPath($e['trace'][0]['file'], $e['trace'][0]['line']), $message); + , $ind, $total, $class, $message); foreach ($e['trace'] as $trace) { - $content .= '
  1. '; + $content .= '
  2. \n"; } - $content .= " \n\n"; + $content .= "\n
    +

    + (%d/%d) + %s +

    +

    %s

    +
    '; if ($trace['function']) { - $content .= sprintf('at %s%s%s(%s)', $this->formatClass($trace['class']), $trace['type'], $trace['function'], $this->formatArgs($trace['args'])); + $content .= sprintf('at %s%s%s(%s)', $this->formatClass($trace['class']), $trace['type'], $trace['function'], $this->formatArgs($trace['args'])); } if (isset($trace['file']) && isset($trace['line'])) { $content .= $this->formatPath($trace['file'], $trace['line']); } - $content .= "\n"; + $content .= "
    \n
    \n"; } } catch (\Exception $e) { // something nasty happened and we cannot throw an exception anymore @@ -253,9 +255,19 @@ public function getContent(FlattenException $exception) } } + $symfonyGhostImageContents = $this->getSymfonyGhostAsSvg(); + return << -

    $title

    +
    +
    +
    +

    $title

    +
    $symfonyGhostImageContents
    +
    +
    +
    + +
    $content
    EOF; @@ -271,48 +283,52 @@ public function getContent(FlattenException $exception) public function getStylesheet(FlattenException $exception) { return <<<'EOF' - .sf-reset { font: 11px Verdana, Arial, sans-serif; color: #333 } - .sf-reset .clear { clear:both; height:0; font-size:0; line-height:0; } - .sf-reset .clear_fix:after { display:block; height:0; clear:both; visibility:hidden; } - .sf-reset .clear_fix { display:inline-block; } - .sf-reset * html .clear_fix { height:1%; } - .sf-reset .clear_fix { display:block; } - .sf-reset, .sf-reset .block { margin: auto } - .sf-reset abbr { border-bottom: 1px dotted #000; cursor: help; } - .sf-reset p { font-size:14px; line-height:20px; color:#868686; padding-bottom:20px } - .sf-reset strong { font-weight:bold; } - .sf-reset a { color:#6c6159; cursor: default; } - .sf-reset a img { border:none; } - .sf-reset a:hover { text-decoration:underline; } - .sf-reset em { font-style:italic; } - .sf-reset h1, .sf-reset h2 { font: 20px Georgia, "Times New Roman", Times, serif } - .sf-reset .exception_counter { background-color: #fff; color: #333; padding: 6px; float: left; margin-right: 10px; float: left; display: block; } - .sf-reset .exception_title { margin-left: 3em; margin-bottom: 0.7em; display: block; } - .sf-reset .exception_message { margin-left: 3em; display: block; } - .sf-reset .traces li { font-size:12px; padding: 2px 4px; list-style-type:decimal; margin-left:20px; } - .sf-reset .block { background-color:#FFFFFF; padding:10px 28px; margin-bottom:20px; - border-bottom-right-radius: 16px; - border-bottom-left-radius: 16px; - border-bottom:1px solid #ccc; - border-right:1px solid #ccc; - border-left:1px solid #ccc; - word-wrap: break-word; - } - .sf-reset .block_exception { background-color:#ddd; color: #333; padding:20px; - border-top-left-radius: 16px; - border-top-right-radius: 16px; - border-top:1px solid #ccc; - border-right:1px solid #ccc; - border-left:1px solid #ccc; - overflow: hidden; - word-wrap: break-word; - } - .sf-reset a { background:none; color:#868686; text-decoration:none; } - .sf-reset a:hover { background:none; color:#313131; text-decoration:underline; } - .sf-reset ol { padding: 10px 0; } - .sf-reset h1 { background-color:#FFFFFF; padding: 15px 28px; margin-bottom: 20px; - border-radius: 10px; - border: 1px solid #ccc; + body { background-color: #F9F9F9; color: #222; font: 14px/1.4 Helvetica, Arial, sans-serif; margin: 0; padding-bottom: 45px; } + + a { cursor: pointer; text-decoration: none; } + a:hover { text-decoration: underline; } + abbr[title] { border-bottom: none; cursor: help; text-decoration: none; } + + code, pre { font: 13px/1.5 Consolas, Monaco, Menlo, "Ubuntu Mono", "Liberation Mono", monospace; } + + table, tr, th, td { background: #FFF; border-collapse: collapse; vertical-align: top; } + table { background: #FFF; border: 1px solid #E0E0E0; box-shadow: 0px 0px 1px rgba(128, 128, 128, .2); margin: 1em 0; width: 100%; } + table th, table td { border: solid #E0E0E0; border-width: 1px 0; padding: 8px 10px; } + table th { background-color: #E0E0E0; font-weight: bold; text-align: left; } + + .hidden-xs-down { display: none; } + .block { display: block; } + .break-long-words { -ms-word-break: break-all; word-break: break-all; word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; } + .text-muted { color: #999; } + + .container { max-width: 1024px; margin: 0 auto; padding: 0 15px; } + .container::after { content: ""; display: table; clear: both; } + + .exception-summary { background: #B0413E; border-bottom: 2px solid rgba(0, 0, 0, 0.1); border-top: 1px solid rgba(0, 0, 0, .3); flex: 0 0 auto; margin-bottom: 30px; } + + .exception-message-wrapper { display: flex; align-items: center; min-height: 70px; } + .exception-message { flex-grow: 1; padding: 30px 0; } + .exception-message, .exception-message a { color: #FFF; font-size: 21px; font-weight: 400; margin: 0; } + .exception-message.long { font-size: 18px; } + .exception-message a { text-decoration: none; } + .exception-message a:hover { text-decoration: underline; } + + .exception-illustration { flex-basis: 111px; flex-shrink: 0; height: 66px; margin-left: 15px; opacity: .7; } + + .trace + .trace { margin-top: 30px; } + .trace-head { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } + .trace-head .trace-class { color: #222; font-size: 18px; font-weight: bold; line-height: 1.3; margin: 0; position: relative; } + + .trace-message { font-size: 14px; font-weight: normal; margin: .5em 0 0; } + + .trace-file-path, .trace-file-path a { margin-top: 3px; color: #999; color: #795da3; color: #B0413E; color: #222; font-size: 13px; } + .trace-class { color: #B0413E; } + .trace-type { padding: 0 2px; } + .trace-method { color: #B0413E; color: #222; font-weight: bold; color: #B0413E; } + .trace-arguments { color: #222; color: #999; font-weight: normal; color: #795da3; color: #777; padding-left: 2px; } + + @media (min-width: 575px) { + .hidden-xs-down { display: initial; } } EOF; } @@ -325,17 +341,9 @@ private function decorate($content, $css) - + - + $content @@ -355,10 +363,10 @@ private function formatPath($path, $line) $fmt = $this->fileLinkFormat; if ($fmt && $link = is_string($fmt) ? strtr($fmt, array('%f' => $path, '%l' => $line)) : $fmt->format($path, $line)) { - return sprintf(' in %s line %d', $this->escapeHtml($link), $file, $line); + return sprintf('in %s (line %d)', $this->escapeHtml($link), $file, $line); } - return sprintf(' in %s line %d', $this->escapeHtml($path), $file, $line); + return sprintf('in %s (line %d)', $this->escapeHtml($path), $file, $line); } /** @@ -399,4 +407,9 @@ private function escapeHtml($str) { return htmlspecialchars($str, ENT_COMPAT | ENT_SUBSTITUTE, $this->charset); } + + private function getSymfonyGhostAsSvg() + { + return ''; + } } diff --git a/src/Symfony/Component/Debug/Tests/ExceptionHandlerTest.php b/src/Symfony/Component/Debug/Tests/ExceptionHandlerTest.php index b3701284d4443..0285eff1346cb 100644 --- a/src/Symfony/Component/Debug/Tests/ExceptionHandlerTest.php +++ b/src/Symfony/Component/Debug/Tests/ExceptionHandlerTest.php @@ -39,8 +39,8 @@ public function testDebug() $handler->sendPhpResponse(new \RuntimeException('Foo')); $response = ob_get_clean(); - $this->assertContains('

    Whoops, looks like something went wrong.

    ', $response); - $this->assertNotContains('

    ', $response); + $this->assertContains('Whoops, looks like something went wrong.', $response); + $this->assertNotContains('
    ', $response); $handler = new ExceptionHandler(true); @@ -48,8 +48,8 @@ public function testDebug() $handler->sendPhpResponse(new \RuntimeException('Foo')); $response = ob_get_clean(); - $this->assertContains('

    Whoops, looks like something went wrong.

    ', $response); - $this->assertContains('

    ', $response); + $this->assertContains('Whoops, looks like something went wrong.', $response); + $this->assertContains('
    ', $response); } public function testStatusCode() @@ -94,7 +94,7 @@ public function testNestedExceptions() $handler->sendPhpResponse(new \RuntimeException('Foo', 0, new \RuntimeException('Bar'))); $response = ob_get_clean(); - $this->assertStringMatchesFormat('%AFoo%ABar%A', $response); + $this->assertStringMatchesFormat('%A

    Foo

    %A

    Bar

    %A', $response); } public function testHandle() diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index 7e03a508561de..b591c5958dae7 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -28,6 +28,7 @@ CHANGELOG will not be supported anymore in 4.0 * deprecated the `DefinitionDecorator` class in favor of `ChildDefinition` * allow config files to be loaded using a glob pattern + * [BC BREAK] the `NullDumper` class is now final 3.2.0 ----- @@ -37,6 +38,7 @@ CHANGELOG * deprecated the ability to set or unset a private service with the `Container::set()` method * deprecated the ability to check for the existence of a private service with the `Container::has()` method * deprecated the ability to request a private service with the `Container::get()` method + * deprecated support for generating a dumped `Container` without populating the method map 3.0.0 ----- diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowireExceptionPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowireExceptionPass.php index 31e407cd630a8..2ee9427a15098 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowireExceptionPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowireExceptionPass.php @@ -21,16 +21,30 @@ class AutowireExceptionPass implements CompilerPassInterface { private $autowirePass; + private $inlineServicePass; - public function __construct(AutowirePass $autowirePass) + public function __construct(AutowirePass $autowirePass, InlineServiceDefinitionsPass $inlineServicePass) { $this->autowirePass = $autowirePass; + $this->inlineServicePass = $inlineServicePass; } public function process(ContainerBuilder $container) { - foreach ($this->autowirePass->getAutowiringExceptions() as $exception) { - if ($container->hasDefinition($exception->getServiceId())) { + // the pass should only be run once + if (null === $this->autowirePass || null === $this->inlineServicePass) { + return; + } + + $inlinedIds = $this->inlineServicePass->getInlinedServiceIds(); + $exceptions = $this->autowirePass->getAutowiringExceptions(); + + // free up references + $this->autowirePass = null; + $this->inlineServicePass = null; + + foreach ($exceptions as $exception) { + if ($container->hasDefinition($exception->getServiceId()) || in_array($exception->getServiceId(), $inlinedIds)) { throw $exception; } } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index 0a6a0dfc1b6b3..b79a656aa8eaf 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -163,7 +163,6 @@ private function doProcessValue($value, $isRoot = false) */ private function getMethodsToAutowire(\ReflectionClass $reflectionClass) { - $found = array(); $methodsToAutowire = array(); foreach ($reflectionClass->getMethods() as $reflectionMethod) { diff --git a/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php b/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php index 8a2ef34f5b8a8..43d65bdfd3c5b 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php @@ -77,7 +77,7 @@ public function getLoggingFormatter() * @param string $type The type of the pass * @param int $priority Used to sort the passes */ - public function addPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/*, $priority = 0*/) + public function addPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/*, int $priority = 0*/) { if (func_num_args() >= 3) { $priority = func_get_arg(2); @@ -85,7 +85,7 @@ public function addPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BE if (__CLASS__ !== get_class($this)) { $r = new \ReflectionMethod($this, __FUNCTION__); if (__CLASS__ !== $r->getDeclaringClass()->getName()) { - @trigger_error(sprintf('Method %s() will have a third `$priority = 0` argument in version 4.0. Not defining it is deprecated since 3.2.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('Method %s() will have a third `int $priority = 0` argument in version 4.0. Not defining it is deprecated since 3.2.', __METHOD__), E_USER_DEPRECATED); } } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php index cec68cbb573a1..b084d7e4dce28 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php @@ -23,6 +23,7 @@ class InlineServiceDefinitionsPass extends AbstractRecursivePass implements RepeatablePassInterface { private $repeatedPass; + private $inlinedServiceIds = array(); /** * {@inheritdoc} @@ -32,6 +33,16 @@ public function setRepeatedPass(RepeatedPass $repeatedPass) $this->repeatedPass = $repeatedPass; } + /** + * Returns an array of all services inlined by this pass. + * + * @return array Service id strings + */ + public function getInlinedServiceIds() + { + return $this->inlinedServiceIds; + } + /** * {@inheritdoc} */ @@ -46,6 +57,7 @@ protected function processValue($value, $isRoot = false) if ($this->isInlineableDefinition($id, $definition, $this->container->getCompiler()->getServiceReferenceGraph())) { $this->container->log($this, sprintf('Inlined service "%s" to "%s".', $id, $this->currentId)); + $this->inlinedServiceIds[] = $id; if ($definition->isShared()) { return $definition; diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php index a3ef360991a25..5ee8f98851438 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php @@ -73,11 +73,11 @@ public function __construct() new RemoveAbstractDefinitionsPass(), new RepeatedPass(array( new AnalyzeServiceReferencesPass(), - new InlineServiceDefinitionsPass(), + $inlinedServicePass = new InlineServiceDefinitionsPass(), new AnalyzeServiceReferencesPass(), new RemoveUnusedDefinitionsPass(), )), - new AutowireExceptionPass($autowirePass), + new AutowireExceptionPass($autowirePass, $inlinedServicePass), new CheckExceptionOnInvalidReferenceBehaviorPass(), )); } @@ -108,7 +108,7 @@ public function getPasses() * * @throws InvalidArgumentException when a pass type doesn't exist */ - public function addPass(CompilerPassInterface $pass, $type = self::TYPE_BEFORE_OPTIMIZATION/*, $priority = 0*/) + public function addPass(CompilerPassInterface $pass, $type = self::TYPE_BEFORE_OPTIMIZATION/*, int $priority = 0*/) { if (func_num_args() >= 3) { $priority = func_get_arg(2); @@ -116,7 +116,7 @@ public function addPass(CompilerPassInterface $pass, $type = self::TYPE_BEFORE_O if (__CLASS__ !== get_class($this)) { $r = new \ReflectionMethod($this, __FUNCTION__); if (__CLASS__ !== $r->getDeclaringClass()->getName()) { - @trigger_error(sprintf('Method %s() will have a third `$priority = 0` argument in version 4.0. Not defining it is deprecated since 3.2.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('Method %s() will have a third `int $priority = 0` argument in version 4.0. Not defining it is deprecated since 3.2.', __METHOD__), E_USER_DEPRECATED); } } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraph.php b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraph.php index 35733c72d0baa..193a37e20b0f6 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraph.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraph.php @@ -86,7 +86,7 @@ public function clear() * @param string $reference * @param bool $lazy */ - public function connect($sourceId, $sourceValue, $destId, $destValue = null, $reference = null/*, $lazy = false*/) + public function connect($sourceId, $sourceValue, $destId, $destValue = null, $reference = null/*, bool $lazy = false*/) { if (func_num_args() >= 6) { $lazy = func_get_arg(5); @@ -94,7 +94,7 @@ public function connect($sourceId, $sourceValue, $destId, $destValue = null, $re if (__CLASS__ !== get_class($this)) { $r = new \ReflectionMethod($this, __FUNCTION__); if (__CLASS__ !== $r->getDeclaringClass()->getName()) { - @trigger_error(sprintf('Method %s() will have a 6th `$lazy = false` argument in version 4.0. Not defining it is deprecated since 3.3.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('Method %s() will have a 6th `bool $lazy = false` argument in version 4.0. Not defining it is deprecated since 3.3.', __METHOD__), E_USER_DEPRECATED); } } $lazy = false; diff --git a/src/Symfony/Component/DependencyInjection/Container.php b/src/Symfony/Component/DependencyInjection/Container.php index 05e038ac5cb63..fdb780f014d3a 100644 --- a/src/Symfony/Component/DependencyInjection/Container.php +++ b/src/Symfony/Component/DependencyInjection/Container.php @@ -228,6 +228,9 @@ public function set($id, $service) public function has($id) { for ($i = 2;;) { + if (isset($this->privates[$id])) { + @trigger_error(sprintf('Checking for the existence of the "%s" private service is deprecated since Symfony 3.2 and won\'t be supported anymore in Symfony 4.0.', $id), E_USER_DEPRECATED); + } if ('service_container' === $id) { return true; } @@ -238,10 +241,6 @@ public function has($id) return true; } - if (isset($this->privates[$id])) { - @trigger_error(sprintf('Checking for the existence of the "%s" private service is deprecated since Symfony 3.2 and won\'t be supported anymore in Symfony 4.0.', $id), E_USER_DEPRECATED); - } - if (isset($this->methodMap[$id])) { return true; } @@ -287,12 +286,16 @@ public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE // this method can be called thousands of times during a request, avoid // calling $this->normalizeId($id) unless necessary. for ($i = 2;;) { + if (isset($this->privates[$id])) { + @trigger_error(sprintf('Requesting the "%s" private service is deprecated since Symfony 3.2 and won\'t be supported anymore in Symfony 4.0.', $id), E_USER_DEPRECATED); + } if ('service_container' === $id) { return $this; } if (isset($this->aliases[$id])) { $id = $this->aliases[$id]; } + // Re-use shared service instance if it exists. if (isset($this->services[$id])) { return $this->services[$id]; @@ -331,9 +334,6 @@ public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE return; } - if (isset($this->privates[$id])) { - @trigger_error(sprintf('Requesting the "%s" private service is deprecated since Symfony 3.2 and won\'t be supported anymore in Symfony 4.0.', $id), E_USER_DEPRECATED); - } $this->loading[$id] = true; diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 30c8614b3e6a7..0675dd6f58071 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -448,7 +448,7 @@ public function loadFromExtension($extension, array $values = array()) * * @return $this */ - public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/*, $priority = 0*/) + public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/*, int $priority = 0*/) { if (func_num_args() >= 3) { $priority = func_get_arg(2); @@ -456,7 +456,7 @@ public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig: if (__CLASS__ !== get_class($this)) { $r = new \ReflectionMethod($this, __FUNCTION__); if (__CLASS__ !== $r->getDeclaringClass()->getName()) { - @trigger_error(sprintf('Method %s() will have a third `$priority = 0` argument in version 4.0. Not defining it is deprecated since 3.2.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('Method %s() will have a third `int $priority = 0` argument in version 4.0. Not defining it is deprecated since 3.2.', __METHOD__), E_USER_DEPRECATED); } } @@ -736,9 +736,6 @@ public function compile(/*$resolveEnvPlaceholders = false*/) $compiler->compile($this); foreach ($this->definitions as $id => $definition) { - if (!$definition->isPublic()) { - $this->privates[$id] = true; - } if ($this->trackResources && $definition->isLazy()) { $this->getReflectionClass($definition->getClass()); } diff --git a/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php index 9a20525f626c0..25ab3078c9a2a 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php @@ -192,8 +192,7 @@ private function findNodes() } if (!$container->hasDefinition($id)) { - $class = get_class('service_container' === $id ? $this->container : $container->get($id)); - $nodes[$id] = array('class' => str_replace('\\', '\\\\', $class), 'attributes' => $this->options['node.instance']); + $nodes[$id] = array('class' => str_replace('\\', '\\\\', get_class($container->get($id))), 'attributes' => $this->options['node.instance']); } } diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 66a6c56bf5cc9..a1fb8950e0871 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -1259,9 +1259,17 @@ private function getServiceConditionals($value) $conditions = array(); foreach ($services as $service) { + if ($this->container->hasDefinition($service) && !$this->container->getDefinition($service)->isPublic()) { + continue; + } + $conditions[] = sprintf("\$this->has('%s')", $service); } + if (!$conditions) { + return ''; + } + return implode(' && ', $conditions); } @@ -1429,24 +1437,29 @@ private function dumpValue($value, $interpolate = true) } if ($value instanceof IteratorArgument) { - $countCode = array(); - $countCode[] = 'function () {'; $operands = array(0); - $code = array(); $code[] = 'new RewindableGenerator(function () {'; - foreach ($value->getValues() as $k => $v) { - ($c = $this->getServiceConditionals($v)) ? $operands[] = "(int) ($c)" : ++$operands[0]; - $v = $this->wrapServiceConditionals($v, sprintf(" yield %s => %s;\n", $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate))); - foreach (explode("\n", $v) as $v) { - if ($v) { - $code[] = ' '.$v; + + if (!$values = $value->getValues()) { + $code[] = ' return new \EmptyIterator();'; + } else { + $countCode = array(); + $countCode[] = 'function () {'; + + foreach ($values as $k => $v) { + ($c = $this->getServiceConditionals($v)) ? $operands[] = "(int) ($c)" : ++$operands[0]; + $v = $this->wrapServiceConditionals($v, sprintf(" yield %s => %s;\n", $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate))); + foreach (explode("\n", $v) as $v) { + if ($v) { + $code[] = ' '.$v; + } } } - } - $countCode[] = sprintf(' return %s;', implode(' + ', $operands)); - $countCode[] = ' }'; + $countCode[] = sprintf(' return %s;', implode(' + ', $operands)); + $countCode[] = ' }'; + } $code[] = sprintf(' }, %s)', count($operands) > 1 ? implode("\n", $countCode) : $operands[0]); diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php index c00c2167e2bab..61978dcd28971 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php @@ -209,6 +209,10 @@ private function addService($definition, $id, \DOMElement $parent) $service->setAttribute('autoconfigure', 'true'); } + if ($definition->isAbstract()) { + $service->setAttribute('abstract', 'true'); + } + if ($callable = $definition->getConfigurator()) { $configurator = $this->document->createElement('configurator'); diff --git a/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/NullDumper.php b/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/NullDumper.php index 30911d3a5e83a..30cbdef0a6ad8 100644 --- a/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/NullDumper.php +++ b/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/NullDumper.php @@ -17,6 +17,8 @@ * Null dumper, negates any proxy code generation for any given service definition. * * @author Marco Pivetta + * + * @final since version 3.3 */ class NullDumper implements DumperInterface { @@ -31,7 +33,7 @@ public function isProxyCandidate(Definition $definition) /** * {@inheritdoc} */ - public function getProxyFactoryCode(Definition $definition, $id) + public function getProxyFactoryCode(Definition $definition, $id, $methodName = null) { return ''; } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireExceptionPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireExceptionPassTest.php index 4859db3a64e0f..092b6401c48ef 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireExceptionPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireExceptionPassTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Compiler\AutowireExceptionPass; use Symfony\Component\DependencyInjection\Compiler\AutowirePass; +use Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\AutowiringFailedException; @@ -29,10 +30,45 @@ public function testThrowsException() ->method('getAutowiringExceptions') ->will($this->returnValue(array($autowireException))); + $inlinePass = $this->getMockBuilder(InlineServiceDefinitionsPass::class) + ->getMock(); + $inlinePass->expects($this->any()) + ->method('getInlinedServiceIds') + ->will($this->returnValue(array())); + $container = new ContainerBuilder(); $container->register('foo_service_id'); - $pass = new AutowireExceptionPass($autowirePass); + $pass = new AutowireExceptionPass($autowirePass, $inlinePass); + + try { + $pass->process($container); + $this->fail('->process() should throw the exception if the service id exists'); + } catch (\Exception $e) { + $this->assertSame($autowireException, $e); + } + } + + public function testThrowExceptionIfServiceInlined() + { + $autowirePass = $this->getMockBuilder(AutowirePass::class) + ->getMock(); + + $autowireException = new AutowiringFailedException('foo_service_id', 'An autowiring exception message'); + $autowirePass->expects($this->any()) + ->method('getAutowiringExceptions') + ->will($this->returnValue(array($autowireException))); + + $inlinePass = $this->getMockBuilder(InlineServiceDefinitionsPass::class) + ->getMock(); + $inlinePass->expects($this->any()) + ->method('getInlinedServiceIds') + ->will($this->returnValue(array('foo_service_id'))); + + // don't register the foo_service_id service + $container = new ContainerBuilder(); + + $pass = new AutowireExceptionPass($autowirePass, $inlinePass); try { $pass->process($container); @@ -52,9 +88,15 @@ public function testNoExceptionIfServiceRemoved() ->method('getAutowiringExceptions') ->will($this->returnValue(array($autowireException))); + $inlinePass = $this->getMockBuilder(InlineServiceDefinitionsPass::class) + ->getMock(); + $inlinePass->expects($this->any()) + ->method('getInlinedServiceIds') + ->will($this->returnValue(array())); + $container = new ContainerBuilder(); - $pass = new AutowireExceptionPass($autowirePass); + $pass = new AutowireExceptionPass($autowirePass, $inlinePass); $pass->process($container); // mark the test as passed diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php index c5e7272235f9f..d241758b04425 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php @@ -252,6 +252,30 @@ public function testProcessDoesNotSetLazyArgumentValuesAfterInlining() $this->assertSame('inline', (string) $values[0]); } + public function testGetInlinedServiceIds() + { + $container = new ContainerBuilder(); + $container + ->register('inlinable.service') + ->setPublic(false) + ; + $container + ->register('non_inlinable.service') + ->setPublic(true) + ; + + $container + ->register('service') + ->setArguments(array(new Reference('inlinable.service'))) + ; + + $inlinePass = new InlineServiceDefinitionsPass(); + $repeatedPass = new RepeatedPass(array(new AnalyzeServiceReferencesPass(), $inlinePass)); + $repeatedPass->process($container); + + $this->assertEquals(array('inlinable.service'), $inlinePass->getInlinedServiceIds()); + } + protected function process(ContainerBuilder $container) { $repeatedPass = new RepeatedPass(array(new AnalyzeServiceReferencesPass(), new InlineServiceDefinitionsPass())); diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 05c549bcce2fe..3558c7bd226e1 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -449,12 +449,17 @@ public function testCreateServiceWithIteratorArgument() $builder->register('bar', 'stdClass'); $builder ->register('lazy_context', 'LazyContext') - ->setArguments(array(new IteratorArgument(array('k1' => new Reference('bar'), new Reference('invalid', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))))) + ->setArguments(array( + new IteratorArgument(array('k1' => new Reference('bar'), new Reference('invalid', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))), + new IteratorArgument(array()), + )) ; $lazyContext = $builder->get('lazy_context'); $this->assertInstanceOf(RewindableGenerator::class, $lazyContext->lazyValues); + $this->assertInstanceOf(RewindableGenerator::class, $lazyContext->lazyEmptyValues); $this->assertCount(1, $lazyContext->lazyValues); + $this->assertCount(0, $lazyContext->lazyEmptyValues); $i = 0; foreach ($lazyContext->lazyValues as $k => $v) { @@ -465,6 +470,13 @@ public function testCreateServiceWithIteratorArgument() // The second argument should have been ignored. $this->assertEquals(1, $i); + + $i = 0; + foreach ($lazyContext->lazyEmptyValues as $k => $v) { + ++$i; + } + + $this->assertEquals(0, $i); } /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php index 99419201c6389..1c1e91a9eb046 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php @@ -147,7 +147,7 @@ public function testGetServiceIds() $sc = new ProjectServiceContainer(); $sc->set('foo', $obj = new \stdClass()); - $this->assertEquals(array('service_container', 'internal', 'bar', 'foo_bar', 'foo.baz', 'circular', 'throw_exception', 'throws_exception_on_service_configuration', 'foo'), $sc->getServiceIds(), '->getServiceIds() returns defined service ids by factory methods in the method map, followed by service ids defined by set()'); + $this->assertEquals(array('service_container', 'internal', 'bar', 'foo_bar', 'foo.baz', 'circular', 'throw_exception', 'throws_exception_on_service_configuration', 'internal_dependency', 'foo'), $sc->getServiceIds(), '->getServiceIds() returns defined service ids by factory methods in the method map, followed by service ids defined by set()'); } /** @@ -453,7 +453,8 @@ public function testUnsetInternalPrivateServiceIsDeprecated() public function testChangeInternalPrivateServiceIsDeprecated() { $c = new ProjectServiceContainer(); - $c->set('internal', new \stdClass()); + $c->set('internal', $internal = new \stdClass()); + $this->assertSame($c->get('internal'), $internal); } /** @@ -463,7 +464,8 @@ public function testChangeInternalPrivateServiceIsDeprecated() public function testCheckExistenceOfAnInternalPrivateServiceIsDeprecated() { $c = new ProjectServiceContainer(); - $c->has('internal'); + $c->get('internal_dependency'); + $this->assertTrue($c->has('internal')); } /** @@ -473,6 +475,7 @@ public function testCheckExistenceOfAnInternalPrivateServiceIsDeprecated() public function testRequestAnInternalSharedPrivateServiceIsDeprecated() { $c = new ProjectServiceContainer(); + $c->get('internal_dependency'); $c->get('internal'); } @@ -504,6 +507,7 @@ class ProjectServiceContainer extends Container 'circular' => 'getCircularService', 'throw_exception' => 'getThrowExceptionService', 'throws_exception_on_service_configuration' => 'getThrowsExceptionOnServiceConfigurationService', + 'internal_dependency' => 'getInternalDependencyService', ); public function __construct() @@ -520,7 +524,7 @@ public function __construct() protected function getInternalService() { - return $this->__internal; + return $this->services['internal'] = $this->__internal; } protected function getBarService() @@ -554,6 +558,15 @@ protected function getThrowsExceptionOnServiceConfigurationService() throw new \Exception('Something was terribly wrong while trying to configure the service!'); } + + protected function getInternalDependencyService() + { + $this->services['internal_dependency'] = $instance = new \stdClass(); + + $instance->internal = isset($this->services['internal']) ? $this->services['internal'] : $this->getInternalService(); + + return $instance; + } } class LegacyProjectServiceContainer extends Container diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index e2269538d4faf..b11201111c1f5 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -19,6 +19,7 @@ use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainerInterface; use Symfony\Component\DependencyInjection\Dumper\PhpDumper; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\Reference; @@ -436,7 +437,10 @@ public function testLazyArgumentProvideGenerator() $container->register('lazy_referenced', 'stdClass'); $container ->register('lazy_context', 'LazyContext') - ->setArguments(array(new IteratorArgument(array('k1' => new Reference('lazy_referenced'), 'k2' => new Reference('service_container'))))) + ->setArguments(array( + new IteratorArgument(array('k1' => new Reference('lazy_referenced'), 'k2' => new Reference('service_container'))), + new IteratorArgument(array()), + )) ; $container->compile(); @@ -447,6 +451,9 @@ public function testLazyArgumentProvideGenerator() $lazyContext = $container->get('lazy_context'); $this->assertInstanceOf(RewindableGenerator::class, $lazyContext->lazyValues); + $this->assertInstanceOf(RewindableGenerator::class, $lazyContext->lazyEmptyValues); + $this->assertCount(2, $lazyContext->lazyValues); + $this->assertCount(0, $lazyContext->lazyEmptyValues); $i = -1; foreach ($lazyContext->lazyValues as $k => $v) { @@ -461,6 +468,8 @@ public function testLazyArgumentProvideGenerator() break; } } + + $this->assertEmpty(iterator_to_array($lazyContext->lazyEmptyValues)); } public function testClosureProxy() @@ -597,4 +606,22 @@ public function testServiceSubscriber() $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_subscriber.php', $dumper->dump()); } + + public function testPrivateWithIgnoreOnInvalidReference() + { + require_once self::$fixturesPath.'/includes/classes.php'; + + $container = new ContainerBuilder(); + $container->register('not_invalid', 'BazClass') + ->setPublic(false); + $container->register('bar', 'BarClass') + ->addMethodCall('setBaz', array(new Reference('not_invalid', SymfonyContainerInterface::IGNORE_ON_INVALID_REFERENCE))); + $container->compile(); + + $dumper = new PhpDumper($container); + eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Private_With_Ignore_On_Invalid_Reference'))); + + $container = new \Symfony_DI_PhpDumper_Test_Private_With_Ignore_On_Invalid_Reference(); + $this->assertInstanceOf('BazClass', $container->get('bar')->getBaz()); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php index 9b2ff54770b9f..3ac6628b737e6 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php @@ -183,4 +183,12 @@ public function testDumpAutowireData() $this->assertEquals(file_get_contents(self::$fixturesPath.'/xml/services24.xml'), $dumper->dump()); } + + public function testDumpAbstractServices() + { + $container = include self::$fixturesPath.'/containers/container_abstract.php'; + $dumper = new XmlDumper($container); + + $this->assertEquals(file_get_contents(self::$fixturesPath.'/xml/services_abstract.xml'), $dumper->dump()); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php index 9a18388be0574..2a31ec6d76e47 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php @@ -134,11 +134,11 @@ ; $container ->register('lazy_context', 'LazyContext') - ->setArguments(array(new IteratorArgument(array('k1' => new Reference('foo.baz'), 'k2' => new Reference('service_container'))))) + ->setArguments(array(new IteratorArgument(array('k1' => new Reference('foo.baz'), 'k2' => new Reference('service_container'))), new IteratorArgument(array()))) ; $container ->register('lazy_context_ignore_invalid_ref', 'LazyContext') - ->setArguments(array(new IteratorArgument(array(new Reference('foo.baz'), new Reference('invalid', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))))) + ->setArguments(array(new IteratorArgument(array(new Reference('foo.baz'), new Reference('invalid', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))), new IteratorArgument(array()))) ; $container ->register('closure_proxy', 'BarClass') diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_abstract.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_abstract.php new file mode 100644 index 0000000000000..9622a273d3806 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_abstract.php @@ -0,0 +1,12 @@ +register('foo', 'Foo') + ->setAbstract(true) +; + +return $container; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php index 8ccbbcd438ca4..717dcdc52e579 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php @@ -88,7 +88,7 @@ public function isProxyCandidate(Definition $definition) return false; } - public function getProxyFactoryCode(Definition $definition, $id) + public function getProxyFactoryCode(Definition $definition, $id, $methodName = null) { return ''; } @@ -102,9 +102,11 @@ public function getProxyCode(Definition $definition) class LazyContext { public $lazyValues; + public $lazyEmptyValues; - public function __construct($lazyValues) + public function __construct($lazyValues, $lazyEmptyValues) { $this->lazyValues = $lazyValues; + $this->lazyEmptyValues = $lazyEmptyValues; } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php index d59f315c2cfcb..b8d5aeafacf24 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php @@ -323,7 +323,9 @@ protected function getLazyContextService() return $this->services['lazy_context'] = new \LazyContext(new RewindableGenerator(function () { yield 'k1' => ${($_ = isset($this->services['foo.baz']) ? $this->services['foo.baz'] : $this->get('foo.baz')) && false ?: '_'}; yield 'k2' => $this; - }, 2)); + }, 2), new RewindableGenerator(function () { + return new \EmptyIterator(); + }, 0)); } /** @@ -343,7 +345,9 @@ protected function getLazyContextIgnoreInvalidRefService() } }, function () { return 1 + (int) ($this->has('invalid')); - })); + }), new RewindableGenerator(function () { + return new \EmptyIterator(); + }, 0)); } /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php index f35ca89f03e18..a519cece6e297 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php @@ -326,7 +326,9 @@ protected function getLazyContextService() return $this->services['lazy_context'] = new \LazyContext(new RewindableGenerator(function () { yield 'k1' => ${($_ = isset($this->services['foo.baz']) ? $this->services['foo.baz'] : $this->get('foo.baz')) && false ?: '_'}; yield 'k2' => $this; - }, 2)); + }, 2), new RewindableGenerator(function () { + return new \EmptyIterator(); + }, 0)); } /** @@ -341,7 +343,9 @@ protected function getLazyContextIgnoreInvalidRefService() { return $this->services['lazy_context_ignore_invalid_ref'] = new \LazyContext(new RewindableGenerator(function () { yield 0 => ${($_ = isset($this->services['foo.baz']) ? $this->services['foo.baz'] : $this->get('foo.baz')) && false ?: '_'}; - }, 1)); + }, 1), new RewindableGenerator(function () { + return new \EmptyIterator(); + }, 0)); } /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml index 792c515995ed4..79fdff4c4e2d0 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml @@ -121,12 +121,14 @@ + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_abstract.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_abstract.xml new file mode 100644 index 0000000000000..334e8b045f237 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_abstract.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml index 519567054e074..bfee29fb605e5 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml @@ -112,10 +112,10 @@ services: factory: ['@factory_simple', getInstance] lazy_context: class: LazyContext - arguments: [!iterator {'k1': '@foo.baz', 'k2': '@service_container'}] + arguments: [!iterator {'k1': '@foo.baz', 'k2': '@service_container'}, !iterator []] lazy_context_ignore_invalid_ref: class: LazyContext - arguments: [!iterator ['@foo.baz', '@?invalid']] + arguments: [!iterator ['@foo.baz', '@?invalid'], !iterator []] closure_proxy: class: BarClass arguments: [!closure_proxy ['@closure_proxy', getBaz]] diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index 3a6e94aae08c4..41d1c48d54bcc 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -273,7 +273,7 @@ public function testParsesIteratorArgument() $lazyDefinition = $container->getDefinition('lazy_context'); - $this->assertEquals(array(new IteratorArgument(array('k1' => new Reference('foo.baz'), 'k2' => new Reference('service_container')))), $lazyDefinition->getArguments(), '->load() parses lazy arguments'); + $this->assertEquals(array(new IteratorArgument(array('k1' => new Reference('foo.baz'), 'k2' => new Reference('service_container'))), new IteratorArgument(array())), $lazyDefinition->getArguments(), '->load() parses lazy arguments'); } public function testParsesTags() @@ -659,7 +659,6 @@ public function testDefaults() $this->assertFalse($container->getDefinition('no_defaults')->isAutowired()); - $this->assertTrue($container->getDefinition('child_def')->isPublic()); $this->assertSame(array('foo' => array(array())), $container->getDefinition('child_def')->getTags()); $this->assertFalse($container->getDefinition('child_def')->isAutowired()); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index 8de64d55f4486..2c655d718998f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -346,7 +346,7 @@ public function testParsesIteratorArgument() $lazyDefinition = $container->getDefinition('lazy_context'); - $this->assertEquals(array(new IteratorArgument(array('k1' => new Reference('foo.baz'), 'k2' => new Reference('service_container')))), $lazyDefinition->getArguments(), '->load() parses lazy arguments'); + $this->assertEquals(array(new IteratorArgument(array('k1' => new Reference('foo.baz'), 'k2' => new Reference('service_container'))), new IteratorArgument(array())), $lazyDefinition->getArguments(), '->load() parses lazy arguments'); } public function testAutowire() diff --git a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php old mode 100755 new mode 100644 diff --git a/src/Symfony/Component/Filesystem/LockHandler.php b/src/Symfony/Component/Filesystem/LockHandler.php index 67e6f8f522c1c..3496faae21336 100644 --- a/src/Symfony/Component/Filesystem/LockHandler.php +++ b/src/Symfony/Component/Filesystem/LockHandler.php @@ -68,8 +68,12 @@ public function lock($blocking = false) return true; } + $error = null; + // Silence error reporting - set_error_handler(function () {}); + set_error_handler(function ($errno, $msg) use (&$error) { + $error = $msg; + }); if (!$this->handle = fopen($this->file, 'r')) { if ($this->handle = fopen($this->file, 'x')) { @@ -82,8 +86,7 @@ public function lock($blocking = false) restore_error_handler(); if (!$this->handle) { - $error = error_get_last(); - throw new IOException($error['message'], 0, null, $this->file); + throw new IOException($error, 0, null, $this->file); } // On Windows, even if PHP doc says the contrary, LOCK_NB works, see diff --git a/src/Symfony/Component/Filesystem/Tests/LockHandlerTest.php b/src/Symfony/Component/Filesystem/Tests/LockHandlerTest.php index 1fc2ebc0c1e6e..0791cebc694b8 100644 --- a/src/Symfony/Component/Filesystem/Tests/LockHandlerTest.php +++ b/src/Symfony/Component/Filesystem/Tests/LockHandlerTest.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Filesystem\Tests; use PHPUnit\Framework\TestCase; +use Symfony\Component\Filesystem\Exception\IOException; +use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Filesystem\LockHandler; class LockHandlerTest extends TestCase @@ -40,6 +42,44 @@ public function testConstructWhenRepositoryIsNotWriteable() new LockHandler('lock', '/'); } + public function testErrorHandlingInLockIfLockPathBecomesUnwritable() + { + // skip test on Windows; PHP can't easily set file as unreadable on Windows + if ('\\' === DIRECTORY_SEPARATOR) { + $this->markTestSkipped('This test cannot run on Windows.'); + } + + $lockPath = sys_get_temp_dir().'/'.uniqid(); + $e = null; + $wrongMessage = null; + + try { + mkdir($lockPath); + + $lockHandler = new LockHandler('lock', $lockPath); + + chmod($lockPath, 0444); + + $lockHandler->lock(); + } catch (IOException $e) { + if (false === strpos($e->getMessage(), 'Permission denied')) { + $wrongMessage = $e->getMessage(); + } else { + $this->addToAssertionCount(1); + } + } catch (\Exception $e) { + } catch (\Throwable $e) { + } + + if (is_dir($lockPath)) { + $fs = new Filesystem(); + $fs->remove($lockPath); + } + + $this->assertInstanceOf('Symfony\Component\Filesystem\Exception\IOException', $e, sprintf('Expected IOException to be thrown, got %s instead.', get_class($e))); + $this->assertNull($wrongMessage, sprintf('Expected exception message to contain "Permission denied", got "%s" instead.', $wrongMessage)); + } + public function testConstructSanitizeName() { $lock = new LockHandler(''); diff --git a/src/Symfony/Component/Finder/Tests/GlobTest.php b/src/Symfony/Component/Finder/Tests/GlobTest.php old mode 100755 new mode 100644 diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php index 7d29b6b88c1af..82f912041265d 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php @@ -40,15 +40,6 @@ class DateTimeToStringTransformer extends BaseDateTimeTransformer */ private $parseFormat; - /** - * Whether to parse by appending a pipe "|" to the parse format. - * - * This only works as of PHP 5.3.7. - * - * @var bool - */ - private $parseUsingPipe; - /** * Transforms a \DateTime instance to a string. * @@ -57,16 +48,14 @@ class DateTimeToStringTransformer extends BaseDateTimeTransformer * @param string $inputTimezone The name of the input timezone * @param string $outputTimezone The name of the output timezone * @param string $format The date format - * @param bool $parseUsingPipe Whether to parse by appending a pipe "|" to the parse format * * @throws UnexpectedTypeException if a timezone is not a string */ - public function __construct($inputTimezone = null, $outputTimezone = null, $format = 'Y-m-d H:i:s', $parseUsingPipe = true) + public function __construct($inputTimezone = null, $outputTimezone = null, $format = 'Y-m-d H:i:s') { parent::__construct($inputTimezone, $outputTimezone); $this->generateFormat = $this->parseFormat = $format; - $this->parseUsingPipe = $parseUsingPipe || null === $parseUsingPipe; // See http://php.net/manual/en/datetime.createfromformat.php // The character "|" in the format makes sure that the parts of a date @@ -76,7 +65,7 @@ public function __construct($inputTimezone = null, $outputTimezone = null, $form // where the time corresponds to the current server time. // With "|" and "Y-m-d", "2010-02-03" becomes "2010-02-03 00:00:00", // which is at least deterministic and thus used here. - if ($this->parseUsingPipe && false === strpos($this->parseFormat, '|')) { + if (false === strpos($this->parseFormat, '|')) { $this->parseFormat .= '|'; } } @@ -145,70 +134,6 @@ public function reverseTransform($value) } try { - // On PHP versions < 5.3.7 we need to emulate the pipe operator - // and reset parts not given in the format to their equivalent - // of the UNIX base timestamp. - if (!$this->parseUsingPipe) { - list($year, $month, $day, $hour, $minute, $second) = explode('-', $dateTime->format('Y-m-d-H-i-s')); - - // Check which of the date parts are present in the pattern - preg_match( - '/('. - '(?P[djDl])|'. - '(?P[FMmn])|'. - '(?P[Yy])|'. - '(?P[ghGH])|'. - '(?Pi)|'. - '(?Ps)|'. - '(?Pz)|'. - '(?PU)|'. - '[^djDlFMmnYyghGHiszU]'. - ')*/', - $this->parseFormat, - $matches - ); - - // preg_match() does not guarantee to set all indices, so - // set them unless given - $matches = array_merge(array( - 'day' => false, - 'month' => false, - 'year' => false, - 'hour' => false, - 'minute' => false, - 'second' => false, - 'dayofyear' => false, - 'timestamp' => false, - ), $matches); - - // Reset all parts that don't exist in the format to the - // corresponding part of the UNIX base timestamp - if (!$matches['timestamp']) { - if (!$matches['dayofyear']) { - if (!$matches['day']) { - $day = 1; - } - if (!$matches['month']) { - $month = 1; - } - } - if (!$matches['year']) { - $year = 1970; - } - if (!$matches['hour']) { - $hour = 0; - } - if (!$matches['minute']) { - $minute = 0; - } - if (!$matches['second']) { - $second = 0; - } - $dateTime->setDate($year, $month, $day); - $dateTime->setTime($hour, $minute, $second); - } - } - if ($this->inputTimezone !== $this->outputTimezone) { $dateTime->setTimezone(new \DateTimeZone($this->inputTimezone)); } diff --git a/src/Symfony/Component/Form/Guess/ValueGuess.php b/src/Symfony/Component/Form/Guess/ValueGuess.php index fe40e020ccab8..9a46207eefe42 100644 --- a/src/Symfony/Component/Form/Guess/ValueGuess.php +++ b/src/Symfony/Component/Form/Guess/ValueGuess.php @@ -18,19 +18,14 @@ */ class ValueGuess extends Guess { - /** - * The guessed value. - * - * @var array - */ private $value; /** * Constructor. * - * @param string $value The guessed value - * @param int $confidence The confidence that the guessed class name - * is correct + * @param string|int|bool|null $value The guessed value + * @param int $confidence The confidence that the guessed class name + * is correct */ public function __construct($value, $confidence) { @@ -42,7 +37,7 @@ public function __construct($value, $confidence) /** * Returns the guessed value. * - * @return mixed + * @return string|int|bool|null */ public function getValue() { diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToStringTransformerTest.php index 7a470537bbf31..6fc0051769dd6 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToStringTransformerTest.php @@ -118,21 +118,9 @@ public function testTransformExpectsDateTime() /** * @dataProvider dataProvider */ - public function testReverseTransformUsingPipe($format, $input, $output) + public function testReverseTransform($format, $input, $output) { - $reverseTransformer = new DateTimeToStringTransformer('UTC', 'UTC', $format, true); - - $output = new \DateTime($output); - - $this->assertDateTimeEquals($output, $reverseTransformer->reverseTransform($input)); - } - - /** - * @dataProvider dataProvider - */ - public function testReverseTransformWithoutUsingPipe($format, $input, $output) - { - $reverseTransformer = new DateTimeToStringTransformer('UTC', 'UTC', $format, false); + $reverseTransformer = new DateTimeToStringTransformer('UTC', 'UTC', $format); $output = new \DateTime($output); diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 4962256376d71..cf812a960f2d4 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -30,12 +30,13 @@ */ class Request { - const HEADER_FORWARDED = 0b00001; - const HEADER_X_FORWARDED_ALL = 0b11110; - const HEADER_X_FORWARDED_FOR = 2; - const HEADER_X_FORWARDED_HOST = 4; - const HEADER_X_FORWARDED_PROTO = 8; - const HEADER_X_FORWARDED_PORT = 16; + const HEADER_FORWARDED = 0b00001; // When using RFC 7239 + const HEADER_X_FORWARDED_FOR = 0b00010; + const HEADER_X_FORWARDED_HOST = 0b00100; + const HEADER_X_FORWARDED_PROTO = 0b01000; + const HEADER_X_FORWARDED_PORT = 0b10000; + const HEADER_X_FORWARDED_ALL = 0b11110; // All "X-Forwarded-*" headers + const HEADER_X_FORWARDED_AWS_ELB = 0b11010; // AWS ELB doesn't send X-Forwarded-Host /** @deprecated since version 3.3, to be removed in 4.0 */ const HEADER_CLIENT_IP = self::HEADER_X_FORWARDED_FOR; diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php index eddb610978b65..74366863f7126 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php @@ -31,7 +31,11 @@ protected function setUp() { parent::setUp(); - if (!extension_loaded('mongo') && !extension_loaded('mongodb')) { + if (extension_loaded('mongodb')) { + if (!class_exists('MongoDB\Client')) { + $this->markTestSkipped('The mongodb/mongodb package is required.'); + } + } elseif (!extension_loaded('mongo')) { $this->markTestSkipped('The Mongo or MongoDB extension is required.'); } @@ -107,7 +111,7 @@ public function testRead() if (phpversion('mongodb')) { $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$gte']); - $this->assertGreaterThanOrEqual(round(((int) $criteria[$this->options['expiry_field']]['$gte']) / 1000), $testTimeout); + $this->assertGreaterThanOrEqual(round((string) $criteria[$this->options['expiry_field']]['$gte'] / 1000), $testTimeout); } else { $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$gte']); $this->assertGreaterThanOrEqual($criteria[$this->options['expiry_field']]['$gte']->sec, $testTimeout); @@ -165,7 +169,7 @@ public function testWrite() $this->assertEquals('bar', $data[$this->options['data_field']]->getData()); $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]); $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]); - $this->assertGreaterThanOrEqual($expectedExpiry, round(((int) $data[$this->options['expiry_field']]) / 1000)); + $this->assertGreaterThanOrEqual($expectedExpiry, round((string) $data[$this->options['expiry_field']] / 1000)); } else { $this->assertEquals('bar', $data[$this->options['data_field']]->bin); $this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]); @@ -288,7 +292,7 @@ public function testGc() ->will($this->returnCallback(function ($criteria) { if (phpversion('mongodb')) { $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$lt']); - $this->assertGreaterThanOrEqual(time() - 1, round(((int) $criteria[$this->options['expiry_field']]['$lt']) / 1000)); + $this->assertGreaterThanOrEqual(time() - 1, round((string) $criteria[$this->options['expiry_field']]['$lt'] / 1000)); } else { $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$lt']); $this->assertGreaterThanOrEqual(time() - 1, $criteria[$this->options['expiry_field']]['$lt']->sec); diff --git a/src/Symfony/Component/HttpKernel/CHANGELOG.md b/src/Symfony/Component/HttpKernel/CHANGELOG.md index 15c61d3829c0c..061f61d172e25 100644 --- a/src/Symfony/Component/HttpKernel/CHANGELOG.md +++ b/src/Symfony/Component/HttpKernel/CHANGELOG.md @@ -10,7 +10,8 @@ CHANGELOG * deprecated the special `SYMFONY__` environment variables * added the possibility to change the query string parameter used by `UriSigner` * deprecated `LazyLoadingFragmentHandler::addRendererService()` - * deprecated `Extension::addClassesToCompile()` + * deprecated `Extension::addClassesToCompile()` and `Extension::getClassesToCompile()` + * deprecated `Psr6CacheClearer::addPool()` 3.2.0 ----- diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/Extension.php b/src/Symfony/Component/HttpKernel/DependencyInjection/Extension.php index 99ae0d4b90109..573e1b4e6b669 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/Extension.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/Extension.php @@ -27,9 +27,15 @@ abstract class Extension extends BaseExtension * Gets the classes to cache. * * @return array An array of classes + * + * @deprecated since version 3.3, to be removed in 4.0. */ public function getClassesToCompile() { + if (PHP_VERSION_ID >= 70000) { + @trigger_error(__METHOD__.'() is deprecated since version 3.3, to be removed in 4.0.', E_USER_DEPRECATED); + } + return $this->classes; } diff --git a/src/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.php b/src/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.php index 09ce50df4d260..437b40bf95953 100644 --- a/src/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.php +++ b/src/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.php @@ -119,7 +119,11 @@ protected function createSubRequest($uri, Request $request) // Sub-request object will point to localhost as client ip and real client ip // will be included into trusted header for client ip try { - if ($trustedHeaderName = Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP, false)) { + if (Request::HEADER_X_FORWARDED_FOR & Request::getTrustedHeaderSet()) { + $currentXForwardedFor = $request->headers->get('X_FORWARDED_FOR', ''); + + $server['HTTP_X_FORWARDED_FOR'] = ($currentXForwardedFor ? $currentXForwardedFor.', ' : '').$request->getClientIp(); + } elseif (method_exists(Request::class, 'getTrustedHeaderName') && $trustedHeaderName = Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP, false)) { $currentXForwardedFor = $request->headers->get($trustedHeaderName, ''); $server['HTTP_'.$trustedHeaderName] = ($currentXForwardedFor ? $currentXForwardedFor.', ' : '').$request->getClientIp(); diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 4a3bb7facbe9e..e2f1df0351ad0 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -61,12 +61,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface private $projectDir; - const VERSION = '3.3.0-RC1'; + const VERSION = '3.3.0'; const VERSION_ID = 30300; const MAJOR_VERSION = 3; const MINOR_VERSION = 3; const RELEASE_VERSION = 0; - const EXTRA_VERSION = 'RC1'; + const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '01/2018'; const END_OF_LIFE = '07/2018'; @@ -82,7 +82,6 @@ public function __construct($environment, $debug) $this->environment = $environment; $this->debug = (bool) $debug; $this->rootDir = $this->getRootDir(); - $this->projectDir = $this->getProjectDir(); $this->name = $this->getName(); if ($this->debug) { @@ -604,7 +603,7 @@ protected function getKernelParameters() return array_merge( array( 'kernel.root_dir' => realpath($this->rootDir) ?: $this->rootDir, - 'kernel.project_dir' => realpath($this->projectDir) ?: $this->projectDir, + 'kernel.project_dir' => realpath($this->getProjectDir()) ?: $this->getProjectDir(), 'kernel.environment' => $this->environment, 'kernel.debug' => $this->debug, 'kernel.name' => $this->name, diff --git a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php index 4cc31d49903b1..d2a27d03aaf37 100644 --- a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\HttpKernel\Tests; use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\BundleInterface; use Symfony\Component\HttpKernel\Config\EnvParametersResource; @@ -761,6 +762,15 @@ public function testSymfonyEnvironmentVariables() unset($_SERVER['SYMFONY__FOO__BAR']); } + public function testProjectDirExtension() + { + $kernel = new CustomProjectDirKernel('test', true); + $kernel->boot(); + + $this->assertSame('foo', $kernel->getProjectDir()); + $this->assertSame('foo', $kernel->getContainer()->getParameter('kernel.project_dir')); + } + /** * Returns a mock for the BundleInterface. * @@ -857,3 +867,34 @@ public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = { } } + +class CustomProjectDirKernel extends Kernel +{ + private $baseDir; + + public function __construct() + { + parent::__construct('test', false); + + $this->baseDir = 'foo'; + } + + public function registerBundles() + { + return array(); + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + } + + public function getProjectDir() + { + return $this->baseDir; + } + + public function getRootDir() + { + return __DIR__.'/Fixtures'; + } +} diff --git a/src/Symfony/Component/Intl/Resources/bin/common.php b/src/Symfony/Component/Intl/Resources/bin/common.php index 2e3c026217737..051d253cf27a2 100644 --- a/src/Symfony/Component/Intl/Resources/bin/common.php +++ b/src/Symfony/Component/Intl/Resources/bin/common.php @@ -68,7 +68,7 @@ function get_icu_version_from_genrb($genrb) return $matches[1]; } -set_exception_handler(function (\Exception $exception) { +set_exception_handler(function (\Throwable $exception) { echo "\n"; $cause = $exception; diff --git a/src/Symfony/Component/Intl/Resources/bin/update-data.php b/src/Symfony/Component/Intl/Resources/bin/update-data.php index fe527efeccc11..0914d26ec608e 100644 --- a/src/Symfony/Component/Intl/Resources/bin/update-data.php +++ b/src/Symfony/Component/Intl/Resources/bin/update-data.php @@ -36,7 +36,7 @@ if ($argc > 3 || 2 === $argc && '-h' === $argv[1]) { bailout(<<<'MESSAGE' -Usage: php update-icu-component.php +Usage: php update-data.php Updates the ICU data for Symfony to the latest version of ICU. diff --git a/src/Symfony/Component/Intl/Tests/DateFormatter/AbstractIntlDateFormatterTest.php b/src/Symfony/Component/Intl/Tests/DateFormatter/AbstractIntlDateFormatterTest.php index 35a377da2e899..ac712b3aa5df2 100644 --- a/src/Symfony/Component/Intl/Tests/DateFormatter/AbstractIntlDateFormatterTest.php +++ b/src/Symfony/Component/Intl/Tests/DateFormatter/AbstractIntlDateFormatterTest.php @@ -318,7 +318,7 @@ public function formatWithTimezoneProvider() /** * @dataProvider formatTimezoneProvider - * @requires PHP 5.5 + * @requires PHP 5.5.10 */ public function testFormatTimezone($pattern, $timezone, $expected) { @@ -423,6 +423,9 @@ public function testFormatWithConstructorTimezone() ); } + /** + * @requires PHP 5.5.10 + */ public function testFormatWithDateTimeZoneGmt() { $formatter = $this->getDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT, new \DateTimeZone('GMT'), IntlDateFormatter::GREGORIAN, 'zzz'); diff --git a/src/Symfony/Component/Ldap/CHANGELOG.md b/src/Symfony/Component/Ldap/CHANGELOG.md new file mode 100644 index 0000000000000..61af4adf151a2 --- /dev/null +++ b/src/Symfony/Component/Ldap/CHANGELOG.md @@ -0,0 +1,7 @@ +CHANGELOG +========= + +3.1.0 +----- + + * The `LdapClient` class is deprecated. Use the `Ldap` class instead. diff --git a/src/Symfony/Component/Ldap/LdapClient.php b/src/Symfony/Component/Ldap/LdapClient.php index 0a68c05875b78..b20c7ea4828d5 100644 --- a/src/Symfony/Component/Ldap/LdapClient.php +++ b/src/Symfony/Component/Ldap/LdapClient.php @@ -18,7 +18,7 @@ * @author Francis Besset * @author Charles Sarrazin * - * @deprecated The LdapClient class will be removed in Symfony 4.0. You should use the Ldap class instead. + * @deprecated since version 3.1, to be removed in 4.0. Use the Ldap class instead. */ final class LdapClient implements LdapClientInterface { diff --git a/src/Symfony/Component/Ldap/LdapClientInterface.php b/src/Symfony/Component/Ldap/LdapClientInterface.php index 020e4b55896f5..0872ee082e813 100644 --- a/src/Symfony/Component/Ldap/LdapClientInterface.php +++ b/src/Symfony/Component/Ldap/LdapClientInterface.php @@ -19,7 +19,7 @@ * @author Grégoire Pineau * @author Charles Sarrazin * - * @deprecated You should use LdapInterface instead + * @deprecated since version 3.1, to be removed in 4.0. Use the LdapInterface instead. */ interface LdapClientInterface extends LdapInterface { diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index a709f4a61d26c..fb12d393abad7 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -320,7 +320,7 @@ public function start(callable $callback = null/*, array $env = array()*/) } if ('\\' === DIRECTORY_SEPARATOR && $this->enhanceWindowsCompatibility) { $this->options['bypass_shell'] = true; - $commandline = $this->prepareWindowsCommandLine($commandline, $envBackup); + $commandline = $this->prepareWindowsCommandLine($commandline, $envBackup, $env); } elseif (!$this->useFileHandles && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { // last exit code is output on the fourth pipe and caught to work around --enable-sigchild $descriptors[3] = array('pipe', 'w'); @@ -1627,7 +1627,7 @@ private function doSignal($signal, $throwException) return true; } - private function prepareWindowsCommandLine($cmd, array &$envBackup) + private function prepareWindowsCommandLine($cmd, array &$envBackup, array &$env = null) { $uid = uniqid('', true); $varCount = 0; @@ -1640,7 +1640,7 @@ private function prepareWindowsCommandLine($cmd, array &$envBackup) [^"%!^]*+ )++ )"/x', - function ($m) use (&$envBackup, &$varCache, &$varCount, $uid) { + function ($m) use (&$envBackup, &$env, &$varCache, &$varCount, $uid) { if (isset($varCache[$m[0]])) { return $varCache[$m[0]]; } @@ -1652,10 +1652,15 @@ function ($m) use (&$envBackup, &$varCache, &$varCount, $uid) { } $value = str_replace(array('!LF!', '"^!"', '"^%"', '"^^"', '""'), array("\n", '!', '%', '^', '"'), $value); - $value = preg_replace('/(\\\\*)"/', '$1$1\\"', $value); - + $value = '"'.preg_replace('/(\\\\*)"/', '$1$1\\"', $value).'"'; $var = $uid.++$varCount; - putenv("$var=\"$value\""); + + if (null === $env) { + putenv("$var=$value"); + } else { + $env[$var] = $value; + } + $envBackup[$var] = false; return $varCache[$m[0]] = '!'.$var.'!'; diff --git a/src/Symfony/Component/Process/Tests/ProcessTest.php b/src/Symfony/Component/Process/Tests/ProcessTest.php index 642df9c4a3c08..ade11138e3b60 100644 --- a/src/Symfony/Component/Process/Tests/ProcessTest.php +++ b/src/Symfony/Component/Process/Tests/ProcessTest.php @@ -1465,6 +1465,19 @@ public function testEscapeArgument($arg) $this->assertSame($arg, $p->getOutput()); } + /** + * @dataProvider provideEscapeArgument + * @group legacy + */ + public function testEscapeArgumentWhenInheritEnvDisabled($arg) + { + $p = new Process(array(self::$phpBin, '-r', 'echo $argv[1];', $arg), null, array('BAR' => 'BAZ')); + $p->inheritEnvironmentVariables(false); + $p->run(); + + $this->assertSame($arg, $p->getOutput()); + } + public function provideEscapeArgument() { yield array('a"b%c%'); diff --git a/src/Symfony/Component/PropertyAccess/CHANGELOG.md b/src/Symfony/Component/PropertyAccess/CHANGELOG.md index 574106e521075..416287e2a0e82 100644 --- a/src/Symfony/Component/PropertyAccess/CHANGELOG.md +++ b/src/Symfony/Component/PropertyAccess/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +3.1.0 +----- + + * deprecated the `StringUtil` class, use `Symfony\Component\Inflector\Inflector` + instead + 2.7.0 ------ diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/validpattern.xml b/src/Symfony/Component/Routing/Tests/Fixtures/validpattern.xml index e8d07350b7a42..dbc72e46ddd4d 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/validpattern.xml +++ b/src/Symfony/Component/Routing/Tests/Fixtures/validpattern.xml @@ -11,14 +11,5 @@ context.getMethod() == "GET" - - MyBundle:Blog:show - GET|POST|put|OpTiOnS - hTTps - \w+ - - context.getMethod() == "GET" - - diff --git a/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php index e8e24fde58698..d24ec79a79c59 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php @@ -70,7 +70,7 @@ public function testLoadWithImport() $routeCollection = $loader->load('validresource.xml'); $routes = $routeCollection->all(); - $this->assertCount(3, $routes, 'Two routes are loaded'); + $this->assertCount(2, $routes, 'Two routes are loaded'); $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes); foreach ($routes as $route) { diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php index d65e8e7cc52e4..b18808eb403de 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php @@ -35,7 +35,7 @@ class LdapBindAuthenticationProviderTest extends TestCase public function testEmptyPasswordShouldThrowAnException() { $userProvider = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserProviderInterface')->getMock(); - $ldap = $this->getMockBuilder('Symfony\Component\Ldap\LdapClientInterface')->getMock(); + $ldap = $this->getMockBuilder(LdapInterface::class)->getMock(); $userChecker = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserCheckerInterface')->getMock(); $provider = new LdapBindAuthenticationProvider($userProvider, $userChecker, 'key', $ldap); diff --git a/src/Symfony/Component/Security/Http/Logout/LogoutUrlGenerator.php b/src/Symfony/Component/Security/Http/Logout/LogoutUrlGenerator.php index 1c37229389946..3f2def8d735cc 100644 --- a/src/Symfony/Component/Security/Http/Logout/LogoutUrlGenerator.php +++ b/src/Symfony/Component/Security/Http/Logout/LogoutUrlGenerator.php @@ -48,7 +48,7 @@ public function __construct(RequestStack $requestStack = null, UrlGeneratorInter * @param CsrfTokenManagerInterface|null $csrfTokenManager A CsrfTokenManagerInterface instance * @param string|null $context The listener context */ - public function registerListener($key, $logoutPath, $csrfTokenId, $csrfParameter, CsrfTokenManagerInterface $csrfTokenManager = null/*, $context = null*/) + public function registerListener($key, $logoutPath, $csrfTokenId, $csrfParameter, CsrfTokenManagerInterface $csrfTokenManager = null/*, string $context = null*/) { if (func_num_args() >= 6) { $context = func_get_arg(5); @@ -56,7 +56,7 @@ public function registerListener($key, $logoutPath, $csrfTokenId, $csrfParameter if (__CLASS__ !== get_class($this)) { $r = new \ReflectionMethod($this, __FUNCTION__); if (__CLASS__ !== $r->getDeclaringClass()->getName()) { - @trigger_error(sprintf('Method %s() will have a sixth `$context = null` argument in version 4.0. Not defining it is deprecated since 3.3.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('Method %s() will have a sixth `string $context = null` argument in version 4.0. Not defining it is deprecated since 3.3.', __METHOD__), E_USER_DEPRECATED); } } diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index db55db06dea56..97d9a5c9f68a4 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -302,7 +302,7 @@ protected function getConstructor(array &$data, $class, array &$context, \Reflec * * @throws RuntimeException */ - protected function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes/*, $format = null*/) + protected function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes/*, string $format = null*/) { if (func_num_args() >= 6) { $format = func_get_arg(5); @@ -310,7 +310,7 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref if (__CLASS__ !== get_class($this)) { $r = new \ReflectionMethod($this, __FUNCTION__); if (__CLASS__ !== $r->getDeclaringClass()->getName()) { - @trigger_error(sprintf('Method %s::%s() will have a 6th `$format = null` argument in version 4.0. Not defining it is deprecated since 3.2.', get_class($this), __FUNCTION__), E_USER_DEPRECATED); + @trigger_error(sprintf('Method %s::%s() will have a 6th `string $format = null` argument in version 4.0. Not defining it is deprecated since 3.2.', get_class($this), __FUNCTION__), E_USER_DEPRECATED); } } diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index 4f9268bd174eb..1040111cfe620 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -26,9 +26,6 @@ public function testDenormalize() $this->assertSame('baz', $normalizedData->baz); } - /** - * @group legacy - */ public function testInstantiateObjectDenormalizer() { $data = array('foo' => 'foo', 'bar' => 'bar', 'baz' => 'baz'); diff --git a/src/Symfony/Component/Validator/CHANGELOG.md b/src/Symfony/Component/Validator/CHANGELOG.md index 7099496cfa1d2..14f7b905a8c32 100644 --- a/src/Symfony/Component/Validator/CHANGELOG.md +++ b/src/Symfony/Component/Validator/CHANGELOG.md @@ -6,6 +6,7 @@ CHANGELOG * added `AddValidatorInitializersPass` * added `AddConstraintValidatorsPass` + * added `ContainerConstraintValidatorFactory` 3.2.0 ----- diff --git a/src/Symfony/Component/Validator/ContainerConstraintValidatorFactory.php b/src/Symfony/Component/Validator/ContainerConstraintValidatorFactory.php new file mode 100644 index 0000000000000..a40be361f1c71 --- /dev/null +++ b/src/Symfony/Component/Validator/ContainerConstraintValidatorFactory.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator; + +use Psr\Container\ContainerInterface; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; +use Symfony\Component\Validator\Exception\ValidatorException; + +/** + * Uses a service container to create constraint validators. + * + * @author Kris Wallsmith + */ +class ContainerConstraintValidatorFactory implements ConstraintValidatorFactoryInterface +{ + private $container; + private $validators; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + $this->validators = array(); + } + + /** + * {@inheritdoc} + * + * @throws ValidatorException When the validator class does not exist + * @throws UnexpectedTypeException When the validator is not an instance of ConstraintValidatorInterface + */ + public function getInstance(Constraint $constraint) + { + $name = $constraint->validatedBy(); + + if (!isset($this->validators[$name])) { + if ($this->container->has($name)) { + $this->validators[$name] = $this->container->get($name); + } else { + if (!class_exists($name)) { + throw new ValidatorException(sprintf('Constraint validator "%s" does not exist or it is not enabled. Check the "validatedBy" method in your constraint class "%s".', $name, get_class($constraint))); + } + + $this->validators[$name] = new $name(); + } + } + + if (!$this->validators[$name] instanceof ConstraintValidatorInterface) { + throw new UnexpectedTypeException($this->validators[$name], ConstraintValidatorInterface::class); + } + + return $this->validators[$name]; + } +} diff --git a/src/Symfony/Component/Validator/Tests/ContainerConstraintValidatorFactoryTest.php b/src/Symfony/Component/Validator/Tests/ContainerConstraintValidatorFactoryTest.php new file mode 100644 index 0000000000000..92091484e2905 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/ContainerConstraintValidatorFactoryTest.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\Component\Validator\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Constraints\Blank as BlankConstraint; +use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\ContainerConstraintValidatorFactory; + +class ContainerConstraintValidatorFactoryTest extends TestCase +{ + public function testGetInstanceCreatesValidator() + { + $class = get_class($this->getMockForAbstractClass(ConstraintValidator::class)); + + $constraint = $this->getMockBuilder(Constraint::class)->getMock(); + $constraint + ->expects($this->once()) + ->method('validatedBy') + ->will($this->returnValue($class)); + + $factory = new ContainerConstraintValidatorFactory(new Container()); + $this->assertInstanceOf($class, $factory->getInstance($constraint)); + } + + public function testGetInstanceReturnsExistingValidator() + { + $factory = new ContainerConstraintValidatorFactory(new Container()); + $v1 = $factory->getInstance(new BlankConstraint()); + $v2 = $factory->getInstance(new BlankConstraint()); + $this->assertSame($v1, $v2); + } + + public function testGetInstanceReturnsService() + { + $service = 'validator_constraint_service'; + $validator = $this->getMockForAbstractClass(ConstraintValidator::class); + + // mock ContainerBuilder b/c it implements TaggedContainerInterface + $container = $this->getMockBuilder(ContainerBuilder::class)->setMethods(array('get', 'has'))->getMock(); + $container + ->expects($this->once()) + ->method('get') + ->with($service) + ->willReturn($validator); + $container + ->expects($this->once()) + ->method('has') + ->with($service) + ->willReturn(true); + + $constraint = $this->getMockBuilder(Constraint::class)->getMock(); + $constraint + ->expects($this->once()) + ->method('validatedBy') + ->will($this->returnValue($service)); + + $factory = new ContainerConstraintValidatorFactory($container); + $this->assertSame($validator, $factory->getInstance($constraint)); + } + + /** + * @expectedException \Symfony\Component\Validator\Exception\ValidatorException + */ + public function testGetInstanceInvalidValidatorClass() + { + $constraint = $this->getMockBuilder(Constraint::class)->getMock(); + $constraint + ->expects($this->once()) + ->method('validatedBy') + ->will($this->returnValue('Fully\\Qualified\\ConstraintValidator\\Class\\Name')); + + $factory = new ContainerConstraintValidatorFactory(new Container()); + $factory->getInstance($constraint); + } +} diff --git a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php index 9811eab5e7e48..8e86b054c05b0 100644 --- a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php @@ -51,7 +51,6 @@ public static function castClosure(\Closure $c, array $a, Stub $stub, $isNested, unset($v->value['position'], $v->value['isVariadic'], $v->value['byReference'], $v); } } - ; if (!($filter & Caster::EXCLUDE_VERBOSE) && $f = $c->getFileName()) { $a[$prefix.'file'] = new LinkStub($f, $c->getStartLine()); diff --git a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php index 8b82c51d11e7c..cf94b1af93039 100644 --- a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php @@ -655,17 +655,17 @@ function showCurrent(state) border: 1px solid #ffa500; border-radius: 3px; } -.sf-dump-search-hidden { +pre.sf-dump .sf-dump-search-hidden { display: none; } -.sf-dump-search-wrapper { +pre.sf-dump .sf-dump-search-wrapper { float: right; font-size: 0; white-space: nowrap; max-width: 100%; text-align: right; } -.sf-dump-search-wrapper > * { +pre.sf-dump .sf-dump-search-wrapper > * { vertical-align: top; box-sizing: border-box; height: 21px; @@ -675,7 +675,7 @@ function showCurrent(state) color: #757575; border: 1px solid #BBB; } -.sf-dump-search-wrapper > input.sf-dump-search-input { +pre.sf-dump .sf-dump-search-wrapper > input.sf-dump-search-input { padding: 3px; height: 21px; font-size: 12px; @@ -685,25 +685,25 @@ function showCurrent(state) border-bottom-left-radius: 3px; color: #000; } -.sf-dump-search-wrapper > .sf-dump-search-input-next, -.sf-dump-search-wrapper > .sf-dump-search-input-previous { +pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-next, +pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-previous { background: #F2F2F2; outline: none; border-left: none; font-size: 0; line-height: 0; } -.sf-dump-search-wrapper > .sf-dump-search-input-next { +pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-next { border-top-right-radius: 3px; border-bottom-right-radius: 3px; } -.sf-dump-search-wrapper > .sf-dump-search-input-next > svg, -.sf-dump-search-wrapper > .sf-dump-search-input-previous > svg { +pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-next > svg, +pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-previous > svg { pointer-events: none; width: 12px; height: 12px; } -.sf-dump-search-wrapper > .sf-dump-search-count { +pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-count { display: inline-block; padding: 0 5px; margin: 0; diff --git a/src/Symfony/Component/Yaml/CHANGELOG.md b/src/Symfony/Component/Yaml/CHANGELOG.md index b44b93892af66..892910c832794 100644 --- a/src/Symfony/Component/Yaml/CHANGELOG.md +++ b/src/Symfony/Component/Yaml/CHANGELOG.md @@ -53,8 +53,8 @@ CHANGELOG ----- * Mappings with a colon (`:`) that is not followed by a whitespace are deprecated - and will lead to a `ParseException` in Symfony 4.0 (e.g. `foo:bar` must be - `foo: bar`). + when the mapping key is not quoted and will lead to a `ParseException` in + Symfony 4.0 (e.g. `foo:bar` must be `foo: bar`). * Added support for parsing PHP constants: diff --git a/src/Symfony/Component/Yaml/Dumper.php b/src/Symfony/Component/Yaml/Dumper.php index a38bce014279a..e26a65a5076d7 100644 --- a/src/Symfony/Component/Yaml/Dumper.php +++ b/src/Symfony/Component/Yaml/Dumper.php @@ -41,6 +41,8 @@ public function __construct($indentation = 4) * Sets the indentation. * * @param int $num The amount of spaces to use for indentation of nested nodes + * + * @deprecated since version 3.1, to be removed in 4.0. Pass the indentation to the constructor instead. */ public function setIndentation($num) { diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index 1004034486c06..7ad53692eb000 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -479,6 +479,7 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = ar } // key + $isKeyQuoted = in_array($mapping[$i], array('"', "'"), true); $key = self::parseScalar($mapping, $flags, array(':', ' '), $i, false, array(), true); if (':' !== $key && false === $i = strpos($mapping, ':', $i)) { @@ -497,8 +498,8 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = ar } } - if (':' !== $key && (!isset($mapping[$i + 1]) || !in_array($mapping[$i + 1], array(' ', ',', '[', ']', '{', '}'), true))) { - @trigger_error('Using a colon that is not followed by an indication character (i.e. " ", ",", "[", "]", "{", "}" is deprecated since version 3.2 and will throw a ParseException in 4.0.', E_USER_DEPRECATED); + if (':' !== $key && !$isKeyQuoted && (!isset($mapping[$i + 1]) || !in_array($mapping[$i + 1], array(' ', ',', '[', ']', '{', '}'), true))) { + @trigger_error('Using a colon after an unquoted mapping key that is not followed by an indication character (i.e. " ", ",", "[", "]", "{", "}") is deprecated since version 3.2 and will throw a ParseException in 4.0.', E_USER_DEPRECATED); } while ($i < $len) { diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index dcedd899bc704..42f61af9de0a4 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -208,7 +208,7 @@ private function doParse($value, $flags) $this->refs[$isRef] = end($data); } } elseif ( - self::preg_match('#^(?P'.Inline::REGEX_QUOTED_STRING.'|(?:![^\s]++\s++)?[^ \'"\[\{!].*?) *\:(\s++(?P.+))?$#u', rtrim($this->currentLine), $values) + self::preg_match('#^(?P'.Inline::REGEX_QUOTED_STRING.'|(?:!?!php/const:)?(?:![^\s]++\s++)?[^ \'"\[\{!].*?) *\:(\s++(?P.+))?$#u', rtrim($this->currentLine), $values) && (false === strpos($values['key'], ' #') || in_array($values['key'][0], array('"', "'"))) ) { if ($context && 'sequence' == $context) { @@ -221,7 +221,14 @@ private function doParse($value, $flags) try { Inline::$parsedLineNumber = $this->getRealCurrentLineNb(); $i = 0; - $key = Inline::parseScalar($values['key'], 0, null, $i, !(Yaml::PARSE_KEYS_AS_STRINGS & $flags)); + $evaluateKey = !(Yaml::PARSE_KEYS_AS_STRINGS & $flags); + + // constants in key will be evaluated anyway + if (isset($values['key'][0]) && '!' === $values['key'][0] && Yaml::PARSE_CONSTANT & $flags) { + $evaluateKey = true; + } + + $key = Inline::parseScalar($values['key'], 0, null, $i, $evaluateKey); } catch (ParseException $e) { $e->setParsedLine($this->getRealCurrentLineNb() + 1); $e->setSnippet($this->currentLine); @@ -368,7 +375,11 @@ private function doParse($value, $flags) foreach ($this->lines as $line) { try { - $parsedLine = Inline::parse($line, $flags, $this->refs); + if (isset($line[0]) && ('"' === $line[0] || "'" === $line[0])) { + $parsedLine = $line; + } else { + $parsedLine = Inline::parse($line, $flags, $this->refs); + } if (!is_string($parsedLine)) { $parseError = true; diff --git a/src/Symfony/Component/Yaml/Tests/InlineTest.php b/src/Symfony/Component/Yaml/Tests/InlineTest.php index a26ffbf9e44c6..5b16a532635d9 100644 --- a/src/Symfony/Component/Yaml/Tests/InlineTest.php +++ b/src/Symfony/Component/Yaml/Tests/InlineTest.php @@ -168,7 +168,7 @@ public function testParseInvalidMappingKeyShouldThrowException() /** * @group legacy - * @expectedDeprecation Using a colon that is not followed by an indication character (i.e. " ", ",", "[", "]", "{", "}" is deprecated since version 3.2 and will throw a ParseException in 4.0. + * @expectedDeprecation Using a colon after an unquoted mapping key that is not followed by an indication character (i.e. " ", ",", "[", "]", "{", "}") is deprecated since version 3.2 and will throw a ParseException in 4.0. * throws \Symfony\Component\Yaml\Exception\ParseException in 4.0 */ public function testParseMappingKeyWithColonNotFollowedBySpace() @@ -391,6 +391,8 @@ public function getTestsForParse() array('{\'foo\': \'bar\', "bar": \'foo: bar\'}', array('foo' => 'bar', 'bar' => 'foo: bar')), array('{\'foo\'\'\': \'bar\', "bar\"": \'foo: bar\'}', array('foo\'' => 'bar', 'bar"' => 'foo: bar')), array('{\'foo: \': \'bar\', "bar: ": \'foo: bar\'}', array('foo: ' => 'bar', 'bar: ' => 'foo: bar')), + array('{"foo:bar": "baz"}', array('foo:bar' => 'baz')), + array('{"foo":"bar"}', array('foo' => 'bar')), // nested sequences and mappings array('[foo, [bar, foo]]', array('foo', array('bar', 'foo'))), @@ -460,6 +462,8 @@ public function getTestsForParseWithMapObjects() array('{\'foo\': \'bar\', "bar": \'foo: bar\'}', (object) array('foo' => 'bar', 'bar' => 'foo: bar')), array('{\'foo\'\'\': \'bar\', "bar\"": \'foo: bar\'}', (object) array('foo\'' => 'bar', 'bar"' => 'foo: bar')), array('{\'foo: \': \'bar\', "bar: ": \'foo: bar\'}', (object) array('foo: ' => 'bar', 'bar: ' => 'foo: bar')), + array('{"foo:bar": "baz"}', (object) array('foo:bar' => 'baz')), + array('{"foo":"bar"}', (object) array('foo' => 'bar')), // nested sequences and mappings array('[foo, [bar, foo]]', array('foo', array('bar', 'foo'))), diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index f64d7589d8d88..a0bef9d528c81 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -1561,8 +1561,18 @@ public function testParseMultiLineString() $this->assertEquals("foo bar\nbaz", $this->parser->parse("foo\nbar\n\nbaz")); } - public function testParseMultiLineMappingValue() + /** + * @dataProvider multiLineDataProvider + */ + public function testParseMultiLineMappingValue($yaml, $expected, $parseError) { + $this->assertEquals($expected, $this->parser->parse($yaml)); + } + + public function multiLineDataProvider() + { + $tests = array(); + $yaml = <<<'EOF' foo: - bar: @@ -1579,7 +1589,43 @@ public function testParseMultiLineMappingValue() ), ); - $this->assertEquals($expected, $this->parser->parse($yaml)); + $tests[] = array($yaml, $expected, false); + + $yaml = <<<'EOF' +bar +"foo" +EOF; + $expected = 'bar "foo"'; + + $tests[] = array($yaml, $expected, false); + + $yaml = <<<'EOF' +bar +"foo +EOF; + $expected = 'bar "foo'; + + $tests[] = array($yaml, $expected, false); + + $yaml = <<<'EOF' +bar + +'foo' +EOF; + $expected = "bar\n'foo'"; + + $tests[] = array($yaml, $expected, false); + + $yaml = <<<'EOF' +bar + +foo' +EOF; + $expected = "bar\nfoo'"; + + $tests[] = array($yaml, $expected, false); + + return $tests; } public function testTaggedInlineMapping() @@ -1773,9 +1819,59 @@ public function testParserCleansUpReferencesBetweenRuns() YAML; $this->parser->parse($yaml); } + + public function testPhpConstantTagMappingKey() + { + $yaml = << array( + 'foo' => array( + 'from' => array( + 'bar', + ), + 'to' => 'baz', + ), + ), + ); + + $this->assertSame($expected, $this->parser->parse($yaml, Yaml::PARSE_CONSTANT)); + } + + public function testPhpConstantTagMappingKeyWithKeysCastToStrings() + { + $yaml = << array( + 'foo' => array( + 'from' => array( + 'bar', + ), + 'to' => 'baz', + ), + ), + ); + + $this->assertSame($expected, $this->parser->parse($yaml, Yaml::PARSE_CONSTANT | Yaml::PARSE_KEYS_AS_STRINGS)); + } } class B { public $b = 'foo'; + + const FOO = 'foo'; + const BAR = 'bar'; + const BAZ = 'baz'; }