diff --git a/.travis.yml b/.travis.yml index 79b484ee7ee95..372d685608963 100644 --- a/.travis.yml +++ b/.travis.yml @@ -161,7 +161,7 @@ before_install: fi tfold ext.apcu tpecl apcu-5.1.6 apcu.so $INI - tfold ext.mongodb tpecl mongodb-1.5.2 mongodb.so $INI + tfold ext.mongodb tpecl mongodb-1.6.0alpha1 mongodb.so $INI tfold ext.amqp tpecl amqp-1.9.3 amqp.so $INI tfold ext.igbinary tpecl igbinary-2.0.6 igbinary.so $INI tfold ext.zookeeper tpecl zookeeper-0.5.0 zookeeper.so $INI @@ -226,7 +226,7 @@ install: break fi phpenv global $PHP - ([[ $deps ]] && cd src/Symfony/Component/HttpFoundation; composer config platform.ext-mongodb 1.5.2; composer require --dev --no-update mongodb/mongodb) + ([[ $deps ]] && cd src/Symfony/Component/HttpFoundation; composer config platform.ext-mongodb 1.6.0; composer require --dev --no-update mongodb/mongodb) tfold 'composer update' $COMPOSER_UP tfold 'phpunit install' ./phpunit install if [[ $deps = high ]]; then diff --git a/CHANGELOG-4.2.md b/CHANGELOG-4.2.md index 5e748a7913537..40c53e2040d3a 100644 --- a/CHANGELOG-4.2.md +++ b/CHANGELOG-4.2.md @@ -7,6 +7,33 @@ in 4.2 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v4.2.0...v4.2.1 +* 4.2.1 (2018-12-06) + + * security #cve-2018-19790 [Security\Http] detect bad redirect targets using backslashes (xabbuh) + * security #cve-2018-19789 [Form] Filter file uploads out of regular form types (nicolas-grekas) + * bug #29481 [TwigBridge] Deprecating legacy Twig paths in DebugCommand and simplifications (yceruto) + * bug #29436 [Cache] Fixed Memcached adapter doClear()to call flush() (raitocz) + * bug #29482 Fixes sprintf(): Too few arguments in MessageFormatter::choiceFormat (stephanedelprat) + * bug #29461 [Contracts] extract LocaleAwareInterface out of TranslatorInterface (nicolas-grekas) + * bug #29446 [VarExporter] fix dumping private properties from abstract classes (nicolas-grekas) + * bug #29441 [Routing] ignore trailing slash for non-GET requests (nicolas-grekas) + * bug #29445 [FrameworkBundle] Fix empty output for debug:autowiring when reflection-docblock is not installed (chalasr) + * bug #29444 [Workflow] Fixed BC break for Workflow metadata (lyrixx) + * bug #29432 [DI] dont inline when lazy edges are found (nicolas-grekas) + * bug #29413 [Serializer] fixed DateTimeNormalizer to maintain microseconds when a different timezone required (rvitaliy) + * bug #29424 [Routing] fix taking verb into account when redirecting (nicolas-grekas) + * bug #29418 [VarExporter] fix dumping protected property from abstract classes (nicolas-grekas) + * bug #29414 [DI] Fix dumping expressions accessing single-use private services (chalasr) + * bug #28853 [LDAP] Add TIMEOUT Option to LDAP Connection Options (lmatte7) + * bug #29399 [FrameworkBundle] define doctrine as default_pdo_provider only if the package is installed (nicolas-grekas) + * bug #29375 [Validator] Allow `ConstraintViolation::__toString()` to expose codes that are not null or emtpy strings (phansys) + * bug #29376 [EventDispatcher] Fix eventListener wrapper loop in TraceableEventDispatcher (jderusse) + * bug #29386 undeprecate the single-colon notation for controllers (fbourigault) + * bug #29393 [DI] fix edge case in InlineServiceDefinitionsPass (nicolas-grekas) + * bug #29394 [Config] fix path exclusion during glob discovery (nicolas-grekas) + * bug #29395 [FrameworkBundle][Messenger] Restore check for messenger serializer default id (ogizanagi) + * bug #29380 [Routing] fix greediness of trailing slash (nicolas-grekas) + * 4.2.0 (2018-11-30) * bug #29343 [Form] Handle all case variants of "nan" when parsing a number (mwhudson, xabbuh) diff --git a/UPGRADE-4.2.md b/UPGRADE-4.2.md index a2fadabc95b14..90cd04d01e69a 100644 --- a/UPGRADE-4.2.md +++ b/UPGRADE-4.2.md @@ -68,6 +68,7 @@ Finder Form ---- + * The `symfony/translation` dependency has been removed - run `composer require symfony/translation` if you need the component * The `getExtendedType()` method of the `FormTypeExtensionInterface` is deprecated and will be removed in 5.0. Type extensions must implement the static `getExtendedTypes()` method instead and return an iterable of extended types. @@ -285,6 +286,23 @@ Messenger ``` * The `EncoderInterface` and `DecoderInterface` interfaces have been replaced by a unified `Symfony\Component\Messenger\Transport\Serialization\SerializerInterface`. Each interface method have been merged untouched into the `Serializer` interface, so you can simply merge your two implementations together and implement the new interface. + * The `HandlerLocator` class was replaced with `Symfony\Component\Messenger\Handler\HandlersLocator`. + + Before: + ```php + new HandlerLocator([ + YourMessage::class => $handlerCallable, + ]); + ``` + + After: + ```php + new HandlersLocator([ + YourMessage::class => [ + $handlerCallable, + ] + ]); + ``` Monolog ------- @@ -364,6 +382,7 @@ TwigBundle Validator --------- + * The `symfony/translation` dependency has been removed - run `composer require symfony/translation` if you need the component * The `checkMX` and `checkHost` options of the `Email` constraint are deprecated * The component is now decoupled from `symfony/translation` and uses `Symfony\Contracts\Translation\TranslatorInterface` instead * The `ValidatorBuilderInterface` has been deprecated and `ValidatorBuilder` made final diff --git a/composer.json b/composer.json index 839e21780b88d..b0fcede7bf746 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,7 @@ "psr/link": "^1.0", "psr/log": "~1.0", "psr/simple-cache": "^1.0", - "symfony/contracts": "^1.0", + "symfony/contracts": "^1.0.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-icu": "~1.0", "symfony/polyfill-mbstring": "~1.0", diff --git a/src/Symfony/Bridge/Doctrine/composer.json b/src/Symfony/Bridge/Doctrine/composer.json index bc828b43e21b6..db7c8ce7a341e 100644 --- a/src/Symfony/Bridge/Doctrine/composer.json +++ b/src/Symfony/Bridge/Doctrine/composer.json @@ -46,7 +46,8 @@ }, "conflict": { "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", - "symfony/dependency-injection": "<3.4" + "symfony/dependency-injection": "<3.4", + "symfony/messenger": "<4.2" }, "suggest": { "symfony/form": "", diff --git a/src/Symfony/Bridge/Twig/Command/DebugCommand.php b/src/Symfony/Bridge/Twig/Command/DebugCommand.php index 072022c0a417e..e9d9eda30e05b 100644 --- a/src/Symfony/Bridge/Twig/Command/DebugCommand.php +++ b/src/Symfony/Bridge/Twig/Command/DebugCommand.php @@ -208,8 +208,8 @@ private function displayGeneralText(SymfonyStyle $io, string $filter = null) $io->table(array('Namespace', 'Paths'), $this->buildTableRows($paths)); } - if ($wronBundles = $this->findWrongBundleOverrides()) { - foreach ($this->buildWarningMessages($wronBundles) as $message) { + if ($wrongBundles = $this->findWrongBundleOverrides()) { + foreach ($this->buildWarningMessages($wrongBundles) as $message) { $io->warning($message); } } @@ -253,13 +253,7 @@ private function getLoaderPaths(string $name = null): array } foreach ($namespaces as $namespace) { - $paths = array_map(function ($path) { - if (null !== $this->projectDir && 0 === strpos($path, $this->projectDir)) { - $path = ltrim(substr($path, \strlen($this->projectDir)), \DIRECTORY_SEPARATOR); - } - - return $path; - }, $loader->getPaths($namespace)); + $paths = array_map(array($this, 'getRelativePath'), $loader->getPaths($namespace)); if (FilesystemLoader::MAIN_NAMESPACE === $namespace) { $namespace = '(None)'; @@ -368,53 +362,38 @@ private function findWrongBundleOverrides(): array if ($this->rootDir && $this->projectDir) { $folders = glob($this->rootDir.'/Resources/*/views', GLOB_ONLYDIR); - $relativePath = ltrim(substr($this->rootDir.'/Resources/', \strlen($this->projectDir)), \DIRECTORY_SEPARATOR); - $bundleNames = array_reduce( - $folders, - function ($carry, $absolutePath) use ($relativePath) { - if (0 === strpos($absolutePath, $this->projectDir)) { - $name = basename(\dirname($absolutePath)); - $path = $relativePath.$name; - $carry[$name] = $path; - } + $relativePath = ltrim(substr($this->rootDir.\DIRECTORY_SEPARATOR.'Resources/', \strlen($this->projectDir)), \DIRECTORY_SEPARATOR); + $bundleNames = array_reduce($folders, function ($carry, $absolutePath) use ($relativePath) { + if (0 === strpos($absolutePath, $this->projectDir)) { + $name = basename(\dirname($absolutePath)); + $path = ltrim($relativePath.$name, \DIRECTORY_SEPARATOR); + $carry[$name] = $path; + + @trigger_error(sprintf('Templates directory "%s" is deprecated since Symfony 4.2, use "%s" instead.', $absolutePath, $this->twigDefaultPath.'/bundles/'.$name), E_USER_DEPRECATED); + } - return $carry; - }, - $bundleNames - ); + return $carry; + }, $bundleNames); } if ($this->twigDefaultPath && $this->projectDir) { $folders = glob($this->twigDefaultPath.'/bundles/*', GLOB_ONLYDIR); - $relativePath = ltrim(substr($this->twigDefaultPath.'/bundles', \strlen($this->projectDir)), \DIRECTORY_SEPARATOR); - $bundleNames = array_reduce( - $folders, - function ($carry, $absolutePath) use ($relativePath) { - if (0 === strpos($absolutePath, $this->projectDir)) { - $path = ltrim(substr($absolutePath, \strlen($this->projectDir)), \DIRECTORY_SEPARATOR); - $name = ltrim(substr($path, \strlen($relativePath)), \DIRECTORY_SEPARATOR); - $carry[$name] = $path; - } - - return $carry; - }, - $bundleNames - ); - } - - if (\count($bundleNames)) { - $notFoundBundles = array_diff_key($bundleNames, $this->bundlesMetadata); - if (\count($notFoundBundles)) { - $alternatives = array(); - foreach ($notFoundBundles as $notFoundBundle => $path) { - $alternatives[$path] = array(); - foreach ($this->bundlesMetadata as $name => $bundle) { - $lev = levenshtein($notFoundBundle, $name); - if ($lev <= \strlen($notFoundBundle) / 3 || false !== strpos($name, $notFoundBundle)) { - $alternatives[$path][] = $name; - } - } + $relativePath = ltrim(substr($this->twigDefaultPath.'/bundles/', \strlen($this->projectDir)), \DIRECTORY_SEPARATOR); + $bundleNames = array_reduce($folders, function ($carry, $absolutePath) use ($relativePath) { + if (0 === strpos($absolutePath, $this->projectDir)) { + $name = basename($absolutePath); + $path = ltrim($relativePath.$name, \DIRECTORY_SEPARATOR); + $carry[$name] = $path; } + + return $carry; + }, $bundleNames); + } + + if ($notFoundBundles = array_diff_key($bundleNames, $this->bundlesMetadata)) { + $alternatives = array(); + foreach ($notFoundBundles as $notFoundBundle => $path) { + $alternatives[$path] = $this->findAlternatives($notFoundBundle, array_keys($this->bundlesMetadata)); } } diff --git a/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php b/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php index ed3fea871e68f..e648a52d84a73 100644 --- a/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php @@ -42,6 +42,52 @@ public function testFilterAndJsonFormatOptions() $this->assertEquals($expected, json_decode($tester->getDisplay(true), true)); } + public function testWarningsWrongBundleOverriding() + { + $bundleMetadata = array( + 'TwigBundle' => 'vendor/twig-bundle/', + 'WebProfilerBundle' => 'vendor/web-profiler-bundle/', + ); + $defaultPath = \dirname(__DIR__).\DIRECTORY_SEPARATOR.'Fixtures'.\DIRECTORY_SEPARATOR.'templates'; + + $tester = $this->createCommandTester(array(), $bundleMetadata, $defaultPath); + $ret = $tester->execute(array('--filter' => 'unknown', '--format' => 'json'), array('decorated' => false)); + + $expected = array('warnings' => array( + 'Path "templates/bundles/UnknownBundle" not matching any bundle found', + 'Path "templates/bundles/WebProfileBundle" not matching any bundle found, did you mean "WebProfilerBundle"?', + )); + + $this->assertEquals(0, $ret, 'Returns 0 in case of success'); + $this->assertEquals($expected, json_decode($tester->getDisplay(true), true)); + } + + /** + * @group legacy + * @expectedDeprecation Templates directory "%sResources/BarBundle/views" is deprecated since Symfony 4.2, use "%stemplates/bundles/BarBundle" instead. + */ + public function testDeprecationForWrongBundleOverridingInLegacyPath() + { + $bundleMetadata = array( + 'TwigBundle' => 'vendor/twig-bundle/', + 'WebProfilerBundle' => 'vendor/web-profiler-bundle/', + ); + $defaultPath = \dirname(__DIR__).\DIRECTORY_SEPARATOR.'Fixtures'.\DIRECTORY_SEPARATOR.'templates'; + $rootDir = \dirname(__DIR__).\DIRECTORY_SEPARATOR.'Fixtures'; + + $tester = $this->createCommandTester(array(), $bundleMetadata, $defaultPath, $rootDir); + $ret = $tester->execute(array('--filter' => 'unknown', '--format' => 'json'), array('decorated' => false)); + + $expected = array('warnings' => array( + 'Path "Resources/BarBundle" not matching any bundle found', + 'Path "templates/bundles/UnknownBundle" not matching any bundle found', + 'Path "templates/bundles/WebProfileBundle" not matching any bundle found, did you mean "WebProfilerBundle"?', + )); + + $this->assertEquals(0, $ret, 'Returns 0 in case of success'); + $this->assertEquals($expected, json_decode($tester->getDisplay(true), true)); + } + /** * @expectedException \Symfony\Component\Console\Exception\InvalidArgumentException * @expectedExceptionMessage Malformed namespaced template name "@foo" (expecting "@namespace/template_name"). @@ -233,7 +279,7 @@ public function getDebugTemplateNameTestData() ); } - private function createCommandTester(array $paths = array()): CommandTester + private function createCommandTester(array $paths = array(), array $bundleMetadata = array(), string $defaultPath = null, string $rootDir = null): CommandTester { $projectDir = \dirname(__DIR__).\DIRECTORY_SEPARATOR.'Fixtures'; $loader = new FilesystemLoader(array(), $projectDir); @@ -246,7 +292,7 @@ private function createCommandTester(array $paths = array()): CommandTester } $application = new Application(); - $application->add(new DebugCommand(new Environment($loader), $projectDir)); + $application->add(new DebugCommand(new Environment($loader), $projectDir, $bundleMetadata, $defaultPath, $rootDir)); $command = $application->find('debug:twig'); return new CommandTester($command); diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubTranslator.php b/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubTranslator.php index 083124d6af70d..bd9191161a7c4 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubTranslator.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubTranslator.php @@ -19,17 +19,4 @@ public function trans($id, array $parameters = array(), $domain = null, $locale { return '[trans]'.$id.'[/trans]'; } - - public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) - { - return '[trans]'.$id.'[/trans]'; - } - - public function setLocale($locale) - { - } - - public function getLocale() - { - } } diff --git a/src/Symfony/Bridge/Twig/Tests/Fixtures/Resources/BarBundle/views/base.html.twig b/src/Symfony/Bridge/Twig/Tests/Fixtures/Resources/BarBundle/views/base.html.twig new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Bridge/Twig/Tests/Fixtures/templates/bundles/UnknownBundle/base.html.twig b/src/Symfony/Bridge/Twig/Tests/Fixtures/templates/bundles/UnknownBundle/base.html.twig new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Bridge/Twig/Tests/Fixtures/templates/bundles/WebProfileBundle/Profiler/base.html.twig b/src/Symfony/Bridge/Twig/Tests/Fixtures/templates/bundles/WebProfileBundle/Profiler/base.html.twig new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Bridge/Twig/TokenParser/TransChoiceTokenParser.php b/src/Symfony/Bridge/Twig/TokenParser/TransChoiceTokenParser.php index 490fb14ff45e5..ea605111cac1c 100644 --- a/src/Symfony/Bridge/Twig/TokenParser/TransChoiceTokenParser.php +++ b/src/Symfony/Bridge/Twig/TokenParser/TransChoiceTokenParser.php @@ -40,7 +40,7 @@ public function parse(Token $token) $lineno = $token->getLine(); $stream = $this->parser->getStream(); - @trigger_error(sprintf('The "transchoice" tag is deprecated since Symfony 4.2, use the "trans" one instead with a "%count%" parameter in %s line %d.', $stream->getSourceContext()->getName(), $lineno), E_USER_DEPRECATED); + @trigger_error(sprintf('The "transchoice" tag is deprecated since Symfony 4.2, use the "trans" one instead with a "%%count%%" parameter in %s line %d.', $stream->getSourceContext()->getName(), $lineno), E_USER_DEPRECATED); $vars = new ArrayExpression(array(), $lineno); diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index 2441cbf9faa3d..ec0d36a2b99e3 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": "^7.1.3", - "symfony/contracts": "^1.0", + "symfony/contracts": "^1.0.2", "twig/twig": "^1.35|^2.4.4" }, "require-dev": { diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php index 3cfca24983347..22f2d41d68161 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php @@ -292,7 +292,7 @@ protected function sortServiceIds(array $serviceIds) */ public static function getClassDescription(string $class, string &$resolvedClass = null): string { - $resolvedClass = null; + $resolvedClass = $class; if (!interface_exists(DocBlockFactoryInterface::class)) { return ''; diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 17f67a0159cc3..12f2092320685 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -13,6 +13,7 @@ use Doctrine\Common\Annotations\Annotation; use Doctrine\Common\Cache\Cache; +use Doctrine\DBAL\Connection; use Symfony\Bundle\FullStack; use Symfony\Component\Asset\Package; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; @@ -882,7 +883,7 @@ private function addCacheSection(ArrayNodeDefinition $rootNode) ->scalarNode('default_psr6_provider')->end() ->scalarNode('default_redis_provider')->defaultValue('redis://localhost')->end() ->scalarNode('default_memcached_provider')->defaultValue('memcached://localhost')->end() - ->scalarNode('default_pdo_provider')->defaultValue('doctrine.dbal.default_connection')->end() + ->scalarNode('default_pdo_provider')->defaultValue(class_exists(Connection::class) ? 'database_connection' : null)->end() ->arrayNode('pools') ->useAttributeAsKey('name') ->prototype('array') @@ -1059,7 +1060,7 @@ function ($a) { }) ->end() ->children() - ->scalarNode('id')->defaultValue('messenger.transport.symfony_serializer')->end() + ->scalarNode('id')->defaultValue(!class_exists(FullStack::class) && class_exists(Serializer::class) ? 'messenger.transport.symfony_serializer' : null)->end() ->scalarNode('format')->defaultValue('json')->end() ->arrayNode('context') ->normalizeKeys(false) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index f4b20ee92d0a7..faaebada1592d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -547,7 +547,7 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ } if ($transition['metadata']) { $transitionsMetadataDefinition->addMethodCall('attach', array( - $transitionDefinition, + new Reference($transitionId), $transition['metadata'], )); } @@ -569,7 +569,7 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ } if ($transition['metadata']) { $transitionsMetadataDefinition->addMethodCall('attach', array( - $transitionDefinition, + new Reference($transitionId), $transition['metadata'], )); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php b/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php index 54f20bcfa175f..84e237626ce2a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php +++ b/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php @@ -100,7 +100,7 @@ public function load($resource, $type = null) if (1 === substr_count($controller, ':')) { $nonDeprecatedNotation = str_replace(':', '::', $controller); - @trigger_error(sprintf('Referencing controllers with a single colon is deprecated since Symfony 4.1, use "%s" instead.', $nonDeprecatedNotation), E_USER_DEPRECATED); + // TODO deprecate this in 5.1 } $route->setDefault('_controller', $controller); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/DataCollectorTranslatorPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/DataCollectorTranslatorPassTest.php index f3e4ce96032a1..481500c2cb986 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/DataCollectorTranslatorPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/DataCollectorTranslatorPassTest.php @@ -111,16 +111,4 @@ class TranslatorWithTranslatorBag implements TranslatorInterface public function trans($id, array $parameters = array(), $domain = null, $locale = null) { } - - public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) - { - } - - public function setLocale($locale) - { - } - - public function getLocale() - { - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index d7e8324b55b5e..9a3b2183e12db 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection; +use Doctrine\DBAL\Connection; use PHPUnit\Framework\TestCase; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Configuration; use Symfony\Bundle\FullStack; @@ -18,6 +19,7 @@ use Symfony\Component\Config\Definition\Processor; use Symfony\Component\Lock\Store\SemaphoreStore; use Symfony\Component\Messenger\MessageBusInterface; +use Symfony\Component\Serializer\Serializer; class ConfigurationTest extends TestCase { @@ -267,7 +269,7 @@ protected static function getBundleDefaultConfig() 'directory' => '%kernel.cache_dir%/pools', 'default_redis_provider' => 'redis://localhost', 'default_memcached_provider' => 'memcached://localhost', - 'default_pdo_provider' => 'doctrine.dbal.default_connection', + 'default_pdo_provider' => class_exists(Connection::class) ? 'database_connection' : null, ), 'workflows' => array( 'enabled' => false, @@ -293,7 +295,7 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor 'routing' => array(), 'transports' => array(), 'serializer' => array( - 'id' => 'messenger.transport.symfony_serializer', + 'id' => !class_exists(FullStack::class) && class_exists(Serializer::class) ? 'messenger.transport.symfony_serializer' : null, 'format' => 'json', 'context' => array(), ), diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_routing.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_routing.php index 020068d6247be..60b68f1fdb3e9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_routing.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_routing.php @@ -3,6 +3,7 @@ $container->loadFromExtension('framework', array( 'serializer' => true, 'messenger' => array( + 'serializer' => 'messenger.transport.symfony_serializer', 'routing' => array( 'Symfony\Component\Messenger\Tests\Fixtures\DummyMessage' => array('amqp', 'audit'), 'Symfony\Component\Messenger\Tests\Fixtures\SecondMessage' => array( diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport.php index 76d9894df75f2..728a1a910c143 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport.php @@ -4,6 +4,7 @@ 'serializer' => true, 'messenger' => array( 'serializer' => array( + 'id' => 'messenger.transport.symfony_serializer', 'format' => 'csv', 'context' => array('enable_max_depth' => true), ), diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport_no_serializer.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport_no_serializer.php index ebddbc4bc6b28..f861e25690805 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport_no_serializer.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport_no_serializer.php @@ -5,6 +5,7 @@ 'enabled' => false, ), 'messenger' => array( + 'serializer' => 'messenger.transport.symfony_serializer', 'transports' => array( 'default' => 'amqp://localhost/%2f/messages', ), diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transports.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transports.php index 3f90bda7f3777..4edc0d880a250 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transports.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transports.php @@ -3,6 +3,7 @@ $container->loadFromExtension('framework', array( 'serializer' => true, 'messenger' => array( + 'serializer' => 'messenger.transport.symfony_serializer', 'transports' => array( 'default' => 'amqp://localhost/%2f/messages', 'customised' => array( diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_routing.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_routing.xml index e3d95fc83f496..2ddc776f479fd 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_routing.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_routing.xml @@ -8,6 +8,7 @@ + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport.xml index 135e6f1b78fb1..36d1ada9b8ec1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport.xml @@ -8,7 +8,7 @@ - + true diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport_no_serializer.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport_no_serializer.xml index 38550f08a749a..290fbcf14e47d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport_no_serializer.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport_no_serializer.xml @@ -8,6 +8,7 @@ + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transports.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transports.xml index 211135570c232..4760368448ff7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transports.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transports.xml @@ -8,6 +8,7 @@ + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_routing.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_routing.yml index 38e1fb431c7d9..1eedbbd03d0e6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_routing.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_routing.yml @@ -1,6 +1,7 @@ framework: serializer: true messenger: + serializer: messenger.transport.symfony_serializer routing: 'Symfony\Component\Messenger\Tests\Fixtures\DummyMessage': [amqp, audit] 'Symfony\Component\Messenger\Tests\Fixtures\SecondMessage': diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport.yml index 7433097eb8ea8..05b2083e0abdc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport.yml @@ -2,6 +2,7 @@ framework: serializer: true messenger: serializer: + id: messenger.transport.symfony_serializer format: csv context: enable_max_depth: true diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport_no_serializer.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport_no_serializer.yml index b4ff40db66f03..bde0d3537d57b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport_no_serializer.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport_no_serializer.yml @@ -2,5 +2,6 @@ framework: serializer: enabled: false messenger: + serializer: messenger.transport.symfony_serializer transports: default: 'amqp://localhost/%2f/messages' diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transports.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transports.yml index 0c38638679598..6d2c535b72f80 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transports.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transports.yml @@ -1,6 +1,7 @@ framework: serializer: true messenger: + serializer: messenger.transport.symfony_serializer transports: default: 'amqp://localhost/%2f/messages' customised: diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index d5690d7628273..e52b2c7c2e0a6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -258,10 +258,8 @@ public function testWorkflows() $this->assertSame('attach', $transitionsMetadataCall[0]); $params = $transitionsMetadataCall[1]; $this->assertCount(2, $params); - $this->assertInstanceOf(Definition::class, $params[0]); - $this->assertSame(Workflow\Transition::class, $params[0]->getClass()); - $this->assertSame(array('submit', 'start', 'travis'), $params[0]->getArguments()); - $this->assertSame(array('title' => 'transition submit title'), $params[1]); + $this->assertInstanceOf(Reference::class, $params[0]); + $this->assertSame('state_machine.pull_request.transition.0', (string) $params[0]); $serviceMarkingStoreWorkflowDefinition = $container->getDefinition('workflow.service_marking_store_workflow'); /** @var Reference $markingStoreRef */ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/DelegatingLoaderTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/DelegatingLoaderTest.php index 2cef381f7272c..f7f4272fed7f9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/DelegatingLoaderTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/DelegatingLoaderTest.php @@ -68,7 +68,6 @@ public function testLoadDefaultOptions() /** * @group legacy * @expectedDeprecation Referencing controllers with foo:bar:baz is deprecated since Symfony 4.1, use "some_parsed::controller" instead. - * @expectedDeprecation Referencing controllers with a single colon is deprecated since Symfony 4.1, use "foo::baz" instead. */ public function testLoad() { diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Fixtures/StubTranslator.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Fixtures/StubTranslator.php index 08c0a476b434a..377a67d6be832 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Fixtures/StubTranslator.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Fixtures/StubTranslator.php @@ -19,17 +19,4 @@ public function trans($id, array $parameters = array(), $domain = null, $locale { return '[trans]'.$id.'[/trans]'; } - - public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) - { - return '[trans]'.$id.'[/trans]'; - } - - public function setLocale($locale) - { - } - - public function getLocale() - { - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index f2b85c5f0c84a..fa14dcc25edf0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -19,8 +19,9 @@ "php": "^7.1.3", "ext-xml": "*", "symfony/cache": "~4.2", - "symfony/dependency-injection": "^4.2", "symfony/config": "~4.2", + "symfony/contracts": "^1.0.2", + "symfony/dependency-injection": "^4.2", "symfony/event-dispatcher": "^4.1", "symfony/http-foundation": "^4.1.2", "symfony/http-kernel": "^4.2", diff --git a/src/Symfony/Bundle/TwigBundle/Resources/views/exception.css.twig b/src/Symfony/Bundle/TwigBundle/Resources/views/exception.css.twig index 4556749de8688..a2091e85aa67a 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/views/exception.css.twig +++ b/src/Symfony/Bundle/TwigBundle/Resources/views/exception.css.twig @@ -84,16 +84,15 @@ thead.sf-toggle-content.sf-toggle-visible, tbody.sf-toggle-content.sf-toggle-vis .sf-toggle-off .icon-open, .sf-toggle-on .icon-close { display: block; } .tab-navigation { margin: 0 0 1em 0; padding: 0; } -.tab-navigation li { background: #FFF; border: 1px solid #DDD; color: #444; cursor: pointer; display: inline-block; font-size: 16px; margin: 0 0 0 -1px; padding: .5em .75em; z-index: 1; } -.tab-navigation li:hover { background: #EEE; } -.tab-navigation li.disabled { background: #F5F5F5; color: #999; } -.tab-navigation li.active { background: #666; border-color: #666; color: #FAFAFA; z-index: 1100; } -.tab-navigation li .badge { background-color: #F5F5F5; color: #777; display: inline-block; font-size: 14px; font-weight: bold; margin-left: 8px; min-width: 10px; padding: 1px 6px; text-align: center; } -.tab-navigation li:hover .badge { background: #FAFAFA; color: #777; } -.tab-navigation li.active .badge { background-color: #444; color: #FFF; } -.tab-navigation li .badge.status-warning { background: #A46A1F; color: #FFF; } -.tab-navigation li .badge.status-error { background: #B0413E; color: #FFF; } +.tab-navigation li { background: var(--tab-background); border: 1px solid var(--table-border); color: var(--tab-color); cursor: pointer; display: inline-block; font-size: 16px; margin: 0 0 0 -1px; padding: .5em .75em; z-index: 1; } +.tab-navigation li .badge { background-color: var(--base-1); color: var(--base-4); display: inline-block; font-size: 14px; font-weight: bold; margin-left: 8px; min-width: 10px; padding: 1px 6px; text-align: center; white-space: nowrap; } +.tab-navigation li.disabled { background: var(--tab-disabled-background); color: var(--tab-disabled-color); } +.tab-navigation li.active { background: var(--tab-active-background); color: var(--tab-active-color); z-index: 1100; } +.tab-navigation li.active .badge { background-color: var(--base-5); color: var(--base-2); } .tab-content > *:first-child { margin-top: 0; } +.tab-navigation li .badge.status-warning { background: var(--color-warning); color: #FFF; } +.tab-navigation li .badge.status-error { background: var(--color-error); color: #FFF; } +.sf-tabs .tab:not(:first-child) { display: none; } [data-filters] { position: relative; } [data-filtered] { cursor: pointer; } @@ -126,7 +125,7 @@ thead.sf-toggle-content.sf-toggle-visible, tbody.sf-toggle-content.sf-toggle-vis .filter-list-level li.active { cursor: n-resize; } .filter-list-level li.last-active { cursor: default; } .filter-list-level li.last-active:before { content: '\2714\00a0'; } -.filter-list-choice li:before { content: '\2714\00a0'; color: var(--tab-background); } +.filter-list-choice li:before { content: '\2714\00a0'; color: transparent; } .filter-list-choice li.active:before { color: unset; } .container { max-width: 1024px; margin: 0 auto; padding: 0 15px; } diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.html.twig index e0de7b570af4d..c0be48a377032 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.html.twig @@ -47,7 +47,7 @@
-

Orphaned events {{ collector.orphanedEvents|length }}

+

Orphaned Events {{ collector.orphanedEvents|length }}

{% if collector.orphanedEvents is empty %}
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 02b77319ac249..720da85750526 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig @@ -177,6 +177,12 @@ color: inherit; text-decoration: inherit; } + h2 + h3.form-data-type { + margin-top: 0; + } + h3.form-data-type + h3 { + margin-top: 1em; + } {% endblock %} @@ -455,9 +461,10 @@ {% macro form_tree_details(name, data, forms_by_hash, show) %} {% import _self as tree %}
-

- {{ name|default('(no name)') }} {% if data.type_class is defined %}({{ profiler_dump(data.type_class) }}){% endif %} -

+

{{ name|default('(no name)') }}

+ {% if data.type_class is defined %} +

{{ profiler_dump(data.type_class) }}

+ {% endif %} {% if data.errors is defined and data.errors|length > 0 %}
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 5f6582ed38677..dd5b970148e68 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig @@ -957,7 +957,7 @@ tr.status-warning td { .filter-list-level li.active { cursor: n-resize; } .filter-list-level li.last-active { cursor: default; } .filter-list-level li.last-active:before { content: '\2714\00a0'; } -.filter-list-choice li:before { content: '\2714\00a0'; color: var(--tab-background); } +.filter-list-choice li:before { content: '\2714\00a0'; color: transparent; } .filter-list-choice li.active:before { color: unset; } {# Twig panel diff --git a/src/Symfony/Bundle/WebProfilerBundle/composer.json b/src/Symfony/Bundle/WebProfilerBundle/composer.json index 0f4d40879d22d..79f4ddb596cc6 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/composer.json +++ b/src/Symfony/Bundle/WebProfilerBundle/composer.json @@ -32,6 +32,7 @@ "conflict": { "symfony/dependency-injection": "<3.4", "symfony/event-dispatcher": "<3.4", + "symfony/messenger": "<4.2", "symfony/var-dumper": "<3.4" }, "autoload": { diff --git a/src/Symfony/Component/Cache/Tests/Adapter/MemcachedAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/MemcachedAdapterTest.php index 85292994fe0a0..4ebe4c879891e 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/MemcachedAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/MemcachedAdapterTest.php @@ -193,6 +193,11 @@ public function provideDsnWithOptions() ); } + public function testClear() + { + $this->assertTrue($this->createCachePool()->clear()); + } + public function testMultiServerDsn() { $dsn = 'memcached:?host[localhost]&host[localhost:12345]&host[/some/memcached.sock:]=3'; diff --git a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php index 534de598e3230..1e1718a4127ef 100644 --- a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php +++ b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php @@ -292,7 +292,7 @@ protected function doDelete(array $ids) */ protected function doClear($namespace) { - return false; + return '' === $namespace && $this->getClient()->flush(); } private function checkResultCode($result) diff --git a/src/Symfony/Component/Config/Resource/GlobResource.php b/src/Symfony/Component/Config/Resource/GlobResource.php index 323352058f701..bd110d72f5d94 100644 --- a/src/Symfony/Component/Config/Resource/GlobResource.php +++ b/src/Symfony/Component/Config/Resource/GlobResource.php @@ -96,9 +96,19 @@ public function getIterator() if (!file_exists($this->prefix) || (!$this->recursive && '' === $this->pattern)) { return; } + $prefix = str_replace('\\', '/', $this->prefix); if (0 !== strpos($this->prefix, 'phar://') && false === strpos($this->pattern, '/**/') && (\defined('GLOB_BRACE') || false === strpos($this->pattern, '{'))) { foreach (glob($this->prefix.$this->pattern, \defined('GLOB_BRACE') ? GLOB_BRACE : 0) as $path) { + if ($this->excludedPrefixes) { + $normalizedPath = str_replace('\\', '/', $path); + do { + if (isset($this->excludedPrefixes[$dirPath = $normalizedPath])) { + continue 2; + } + } while ($prefix !== $dirPath && $dirPath !== $normalizedPath = \dirname($dirPath)); + } + if (is_file($path)) { yield $path => new \SplFileInfo($path); } @@ -145,9 +155,19 @@ function (\SplFileInfo $file, $path) { $prefixLen = \strlen($this->prefix); foreach ($finder->followLinks()->sortByName()->in($this->prefix) as $path => $info) { - if (preg_match($regex, substr(str_replace('\\', '/', $path), $prefixLen)) && $info->isFile()) { - yield $path => $info; + $normalizedPath = str_replace('\\', '/', $path); + if (!preg_match($regex, substr($normalizedPath, $prefixLen)) || !$info->isFile()) { + continue; } + if ($this->excludedPrefixes) { + do { + if (isset($this->excludedPrefixes[$dirPath = $normalizedPath])) { + continue 2; + } + } while ($prefix !== $dirPath && $dirPath !== $normalizedPath = \dirname($dirPath)); + } + + yield $path => $info; } } diff --git a/src/Symfony/Component/Config/Tests/Resource/GlobResourceTest.php b/src/Symfony/Component/Config/Tests/Resource/GlobResourceTest.php index ac7ae5bd7507f..188b5572ecb42 100644 --- a/src/Symfony/Component/Config/Tests/Resource/GlobResourceTest.php +++ b/src/Symfony/Component/Config/Tests/Resource/GlobResourceTest.php @@ -73,6 +73,20 @@ public function testIteratorSkipsFoldersForGivenExcludedPrefixes() $this->assertArrayNotHasKey($file, $paths); } + public function testIteratorSkipsSubfoldersForGivenExcludedPrefixes() + { + $dir = \dirname(__DIR__).\DIRECTORY_SEPARATOR.'Fixtures'; + $resource = new GlobResource($dir, '/*Exclude/*', true, false, array($dir.\DIRECTORY_SEPARATOR.'Exclude' => true)); + + $paths = iterator_to_array($resource); + + $file = $dir.\DIRECTORY_SEPARATOR.'Exclude'.\DIRECTORY_SEPARATOR.'AnExcludedFile.txt'; + $this->assertArrayNotHasKey($file, $paths); + + $file = $dir.\DIRECTORY_SEPARATOR.'Exclude'.\DIRECTORY_SEPARATOR.'ExcludeToo'.\DIRECTORY_SEPARATOR.'AnotheExcludedFile.txt'; + $this->assertArrayNotHasKey($file, $paths); + } + public function testIteratorSkipsFoldersWithForwardSlashForGivenExcludedPrefixes() { $dir = \dirname(__DIR__).\DIRECTORY_SEPARATOR.'Fixtures'; diff --git a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php index d4766a16ec0b2..8681c56f69fcc 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php @@ -165,10 +165,14 @@ private function isInlineableDefinition($id, Definition $definition) } if (!$definition->isShared()) { + if (!$this->graph->hasNode($id)) { + return true; + } + foreach ($this->graph->getNode($id)->getInEdges() as $edge) { $srcId = $edge->getSourceNode()->getId(); $this->connectedIds[$srcId] = true; - if ($edge->isWeak()) { + if ($edge->isWeak() || $edge->isLazy()) { return false; } } @@ -196,7 +200,7 @@ private function isInlineableDefinition($id, Definition $definition) $isReferencedByConstructor = $isReferencedByConstructor || $edge->isReferencedByConstructor(); $srcId = $edge->getSourceNode()->getId(); $this->connectedIds[$srcId] = true; - if ($edge->isWeak()) { + if ($edge->isWeak() || $edge->isLazy()) { return false; } $srcIds[$srcId] = true; diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 204a991850b74..71accf4f8c9a4 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -597,9 +597,7 @@ private function addServiceConfigurator(Definition $definition, string $variable if ($callable[0] instanceof Reference || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0])) ) { - $callable[0] = $this->dumpValue($callable[0]); - - return sprintf(' '.('$' === $callable[0][0] ? '%s' : '(%s)')."->%s(\$%s);\n", $callable[0], $callable[1], $variableName); + return sprintf(" %s->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); } $class = $this->dumpValue($callable[0]); @@ -1639,6 +1637,7 @@ private function getServiceCall(string $id, Reference $reference = null): string if ($definition->isShared() && !isset($this->singleUsePrivateIds[$id])) { $code = sprintf('$this->%s[\'%s\'] = %s', $definition->isPublic() ? 'services' : 'privates', $id, $code); } + $code = "($code)"; } elseif ($this->asFiles && !$this->isHotPath($definition)) { $code = sprintf("\$this->load('%s.php')", $this->generateMethodName($id)); if (!$definition->isShared()) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 9798d849fa999..cb41a6d33fb3f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -799,7 +799,7 @@ public function testExpressionReferencingPrivateService() ->setPublic(false); $container->register('public_foo', 'stdClass') ->setPublic(true) - ->addArgument(new Expression('service("private_foo")')); + ->addArgument(new Expression('service("private_foo").bar')); $container->compile(); $dumper = new PhpDumper($container); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt index 126d5fba32cc7..6d305e570f008 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt @@ -338,7 +338,7 @@ use Symfony\Component\DependencyInjection\Exception\RuntimeException; return $this->services['tagged_iterator'] = new \Bar(new RewindableGenerator(function () { yield 0 => ($this->services['foo'] ?? $this->load('getFooService.php')); - yield 1 => ($this->privates['tagged_iterator_foo'] ?? $this->privates['tagged_iterator_foo'] = new \Bar()); + yield 1 => ($this->privates['tagged_iterator_foo'] ?? ($this->privates['tagged_iterator_foo'] = new \Bar())); }, 2)); [Container%s/getThrowingOneService.php] => services['tagged_iterator'] = new \Bar(new RewindableGenerator(function () { yield 0 => ($this->services['foo'] ?? $this->getFooService()); - yield 1 => ($this->privates['tagged_iterator_foo'] ?? $this->privates['tagged_iterator_foo'] = new \Bar()); + yield 1 => ($this->privates['tagged_iterator_foo'] ?? ($this->privates['tagged_iterator_foo'] = new \Bar())); }, 2)); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php index 7d2970de589a0..1f03714232cbe 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php @@ -103,7 +103,7 @@ protected function getBar3Service() { $this->services['bar3'] = $instance = new \BarCircular(); - $a = ($this->services['foobar3'] ?? $this->services['foobar3'] = new \FoobarCircular()); + $a = ($this->services['foobar3'] ?? ($this->services['foobar3'] = new \FoobarCircular())); $instance->addFoobar($a, $a); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_env_in_id.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_env_in_id.php index de90f200d6bfe..969137450a9da 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_env_in_id.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_env_in_id.php @@ -59,7 +59,7 @@ public function getRemovedIds() */ protected function getBarService() { - return $this->services['bar'] = new \stdClass(($this->privates['bar_%env(BAR)%'] ?? $this->privates['bar_%env(BAR)%'] = new \stdClass())); + return $this->services['bar'] = new \stdClass(($this->privates['bar_%env(BAR)%'] ?? ($this->privates['bar_%env(BAR)%'] = new \stdClass()))); } /** @@ -69,7 +69,7 @@ protected function getBarService() */ protected function getFooService() { - return $this->services['foo'] = new \stdClass(($this->privates['bar_%env(BAR)%'] ?? $this->privates['bar_%env(BAR)%'] = new \stdClass()), array('baz_'.$this->getEnv('string:BAR') => new \stdClass())); + return $this->services['foo'] = new \stdClass(($this->privates['bar_%env(BAR)%'] ?? ($this->privates['bar_%env(BAR)%'] = new \stdClass())), array('baz_'.$this->getEnv('string:BAR') => new \stdClass())); } public function getParameter($name) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_errored_definition.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_errored_definition.php index 9620bb599174f..22d6ebbf13ec3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_errored_definition.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_errored_definition.php @@ -391,7 +391,7 @@ protected function getTaggedIteratorService() { return $this->services['tagged_iterator'] = new \Bar(new RewindableGenerator(function () { yield 0 => ($this->services['foo'] ?? $this->getFooService()); - yield 1 => ($this->privates['tagged_iterator_foo'] ?? $this->privates['tagged_iterator_foo'] = new \Bar()); + yield 1 => ($this->privates['tagged_iterator_foo'] ?? ($this->privates['tagged_iterator_foo'] = new \Bar())); }, 2)); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator.php index 4edbb3d7cd55c..4ea482ee2236f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator.php @@ -65,7 +65,7 @@ public function getRemovedIds() */ protected function getBarServiceService() { - return $this->services['bar_service'] = new \stdClass(($this->privates['baz_service'] ?? $this->privates['baz_service'] = new \stdClass())); + return $this->services['bar_service'] = new \stdClass(($this->privates['baz_service'] ?? ($this->privates['baz_service'] = new \stdClass()))); } /** @@ -78,7 +78,7 @@ protected function getFooServiceService() return $this->services['foo_service'] = new \Symfony\Component\DependencyInjection\ServiceLocator(array('bar' => function () { return ($this->services['bar_service'] ?? $this->getBarServiceService()); }, 'baz' => function (): \stdClass { - return ($this->privates['baz_service'] ?? $this->privates['baz_service'] = new \stdClass()); + return ($this->privates['baz_service'] ?? ($this->privates['baz_service'] = new \stdClass())); }, 'nil' => function () { return NULL; })); @@ -122,7 +122,7 @@ protected function getTranslator_Loader3Service() protected function getTranslator1Service() { return $this->services['translator_1'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator(new \Symfony\Component\DependencyInjection\ServiceLocator(array('translator.loader_1' => function () { - return ($this->services['translator.loader_1'] ?? $this->services['translator.loader_1'] = new \stdClass()); + return ($this->services['translator.loader_1'] ?? ($this->services['translator.loader_1'] = new \stdClass())); }))); } @@ -134,10 +134,10 @@ protected function getTranslator1Service() protected function getTranslator2Service() { $this->services['translator_2'] = $instance = new \Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator(new \Symfony\Component\DependencyInjection\ServiceLocator(array('translator.loader_2' => function () { - return ($this->services['translator.loader_2'] ?? $this->services['translator.loader_2'] = new \stdClass()); + return ($this->services['translator.loader_2'] ?? ($this->services['translator.loader_2'] = new \stdClass())); }))); - $instance->addResource('db', ($this->services['translator.loader_2'] ?? $this->services['translator.loader_2'] = new \stdClass()), 'nl'); + $instance->addResource('db', ($this->services['translator.loader_2'] ?? ($this->services['translator.loader_2'] = new \stdClass())), 'nl'); return $instance; } @@ -150,10 +150,10 @@ protected function getTranslator2Service() protected function getTranslator3Service() { $this->services['translator_3'] = $instance = new \Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator(new \Symfony\Component\DependencyInjection\ServiceLocator(array('translator.loader_3' => function () { - return ($this->services['translator.loader_3'] ?? $this->services['translator.loader_3'] = new \stdClass()); + return ($this->services['translator.loader_3'] ?? ($this->services['translator.loader_3'] = new \stdClass())); }))); - $a = ($this->services['translator.loader_3'] ?? $this->services['translator.loader_3'] = new \stdClass()); + $a = ($this->services['translator.loader_3'] ?? ($this->services['translator.loader_3'] = new \stdClass())); $instance->addResource('db', $a, 'nl'); $instance->addResource('db', $a, 'en'); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_frozen.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_frozen.php index 1fe2259fa85d9..559a3ecebfa30 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_frozen.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_frozen.php @@ -56,7 +56,7 @@ public function getRemovedIds() */ protected function getBarServiceService() { - return $this->services['bar_service'] = new \stdClass(($this->privates['baz_service'] ?? $this->privates['baz_service'] = new \stdClass())); + return $this->services['bar_service'] = new \stdClass(($this->privates['baz_service'] ?? ($this->privates['baz_service'] = new \stdClass()))); } /** @@ -66,6 +66,6 @@ protected function getBarServiceService() */ protected function getFooServiceService() { - return $this->services['foo_service'] = new \stdClass(($this->privates['baz_service'] ?? $this->privates['baz_service'] = new \stdClass())); + return $this->services['foo_service'] = new \stdClass(($this->privates['baz_service'] ?? ($this->privates['baz_service'] = new \stdClass()))); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_in_expression.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_in_expression.php index 1d2a3b21b8bbf..db3a5a28153e8 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_in_expression.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_in_expression.php @@ -56,6 +56,6 @@ public function getRemovedIds() */ protected function getPublicFooService() { - return $this->services['public_foo'] = new \stdClass(new \stdClass()); + return $this->services['public_foo'] = new \stdClass((new \stdClass())->bar); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_uninitialized_ref.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_uninitialized_ref.php index 828266d6bc2e0..f18de622b2951 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_uninitialized_ref.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_uninitialized_ref.php @@ -96,7 +96,7 @@ protected function getBazService() { $this->services['baz'] = $instance = new \stdClass(); - $instance->foo3 = ($this->privates['foo3'] ?? $this->privates['foo3'] = new \stdClass()); + $instance->foo3 = ($this->privates['foo3'] ?? ($this->privates['foo3'] = new \stdClass())); return $instance; } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php index 32e404cc5b149..87971eadd8369 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php @@ -18,7 +18,6 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Loader\FileLoader; use Symfony\Component\DependencyInjection\Loader\IniFileLoader; use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; @@ -217,32 +216,20 @@ public function testRegisterClassesWithBadPrefix() } /** - * @dataProvider getIncompatibleExcludeTests + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid "exclude" pattern when importing classes for "Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\": make sure your "exclude" pattern (yaml/*) is a subset of the "resource" pattern (Prototype/*) */ - public function testRegisterClassesWithIncompatibleExclude($resourcePattern, $excludePattern) + public function testRegisterClassesWithIncompatibleExclude() { $container = new ContainerBuilder(); $loader = new TestFileLoader($container, new FileLocator(self::$fixturesPath.'/Fixtures')); - try { - $loader->registerClasses( - new Definition(), - 'Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\\', - $resourcePattern, - $excludePattern - ); - } catch (InvalidArgumentException $e) { - $this->assertEquals( - sprintf('Invalid "exclude" pattern when importing classes for "Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\": make sure your "exclude" pattern (%s) is a subset of the "resource" pattern (%s)', $excludePattern, $resourcePattern), - $e->getMessage() - ); - } - } - - public function getIncompatibleExcludeTests() - { - yield array('Prototype/*', 'yaml/*', false); - yield array('Prototype/OtherDir/*', 'Prototype/*', false); + $loader->registerClasses( + new Definition(), + 'Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\\', + 'Prototype/*', + 'yaml/*' + ); } } diff --git a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php index 083bd39e96d93..92000b6bf5b28 100644 --- a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php +++ b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php @@ -134,19 +134,24 @@ public function dispatch($eventName, Event $event = null) } $this->preProcess($eventName); - $this->preDispatch($eventName, $event); - - $e = $this->stopwatch->start($eventName, 'section'); - - $this->dispatcher->dispatch($eventName, $event); - - if ($e->isStarted()) { - $e->stop(); + try { + $this->preDispatch($eventName, $event); + try { + $e = $this->stopwatch->start($eventName, 'section'); + try { + $this->dispatcher->dispatch($eventName, $event); + } finally { + if ($e->isStarted()) { + $e->stop(); + } + } + } finally { + $this->postDispatch($eventName, $event); + } + } finally { + $this->postProcess($eventName); } - $this->postDispatch($eventName, $event); - $this->postProcess($eventName); - return $event; } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php index 1b699d51e84ab..56fcfe17e9bf7 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php @@ -105,6 +105,7 @@ public function configureOptions(OptionsResolver $resolver) 'data_class' => $dataClass, 'empty_data' => $emptyData, 'multiple' => false, + 'allow_file_upload' => true, )); } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php index b52f382bf71fa..decc9e380db7a 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php @@ -180,6 +180,7 @@ public function configureOptions(OptionsResolver $resolver) 'attr' => array(), 'post_max_size_message' => 'The uploaded file was too large. Please try to upload a smaller file.', 'upload_max_size_message' => $uploadMaxSizeMessage, // internal + 'allow_file_upload' => false, 'help' => null, 'help_attr' => array(), )); diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php index 848025dff3279..26b966c72e027 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -532,6 +532,11 @@ public function submit($submittedData, $clearMissing = true) $submittedData = null; } elseif (is_scalar($submittedData)) { $submittedData = (string) $submittedData; + } elseif ($this->config->getOption('allow_file_upload')) { + // no-op + } elseif ($this->config->getRequestHandler()->isFileUpload($submittedData)) { + $submittedData = null; + $this->transformationFailure = new TransformationFailedException('Submitted data was expected to be text or number, file upload given.'); } $dispatcher = $this->config->getEventDispatcher(); @@ -541,6 +546,10 @@ public function submit($submittedData, $clearMissing = true) $viewData = null; try { + if (null !== $this->transformationFailure) { + throw $this->transformationFailure; + } + // Hook to change content of the data submitted by the browser if ($dispatcher->hasListeners(FormEvents::PRE_SUBMIT)) { $event = new FormEvent($this, $submittedData); diff --git a/src/Symfony/Component/Form/Tests/CompoundFormTest.php b/src/Symfony/Component/Form/Tests/CompoundFormTest.php index b631ee01e2c47..da527125ef3d1 100644 --- a/src/Symfony/Component/Form/Tests/CompoundFormTest.php +++ b/src/Symfony/Component/Form/Tests/CompoundFormTest.php @@ -709,7 +709,7 @@ public function testSubmitPostOrPutRequestWithSingleChildForm($method) 'REQUEST_METHOD' => $method, )); - $form = $this->getBuilder('image') + $form = $this->getBuilder('image', null, null, array('allow_file_upload' => true)) ->setMethod($method) ->setRequestHandler(new HttpFoundationRequestHandler()) ->getForm(); @@ -1081,6 +1081,21 @@ public function testDisabledButtonIsNotSubmitted() $this->assertFalse($submit->isSubmitted()); } + public function testFileUpload() + { + $reqHandler = new HttpFoundationRequestHandler(); + $this->form->add($this->getBuilder('foo')->setRequestHandler($reqHandler)->getForm()); + $this->form->add($this->getBuilder('bar')->setRequestHandler($reqHandler)->getForm()); + + $this->form->submit(array( + 'foo' => 'Foo', + 'bar' => new UploadedFile(__FILE__, 'upload.png', 'image/png', UPLOAD_ERR_OK), + )); + + $this->assertSame('Submitted data was expected to be text or number, file upload given.', $this->form->get('bar')->getTransformationFailure()->getMessage()); + $this->assertNull($this->form->get('bar')->getData()); + } + protected function createForm() { return $this->getBuilder() diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json index 3baeb416d41e3..eb7f21af962e4 100644 --- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json +++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json @@ -28,6 +28,7 @@ "parent": { "Symfony\\Component\\Form\\Extension\\Core\\Type\\FormType": [ "action", + "allow_file_upload", "attr", "auto_initialize", "block_name", diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt index 2da8d91ed5987..500a6e0dd2b2f 100644 --- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt +++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt @@ -8,15 +8,16 @@ Symfony\Component\Form\Extension\Core\Type\ChoiceType (Block prefix: "choice") choice_attr FormType FormType FormTypeCsrfExtension choice_label -------------------- ------------------------- ----------------------- choice_loader compound action csrf_field_name - choice_name data_class attr csrf_message - choice_translation_domain empty_data auto_initialize csrf_protection - choice_value error_bubbling block_name csrf_token_id - choices trim by_reference csrf_token_manager - expanded data - group_by disabled - multiple help - placeholder help_attr - preferred_choices inherit_data + choice_name data_class allow_file_upload csrf_message + choice_translation_domain empty_data attr csrf_protection + choice_value error_bubbling auto_initialize csrf_token_id + choices trim block_name csrf_token_manager + expanded by_reference + group_by data + multiple disabled + placeholder help + preferred_choices help_attr + inherit_data label label_attr label_format diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.json b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.json index af7911f67f909..eb93423adf651 100644 --- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.json +++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.json @@ -4,6 +4,7 @@ "options": { "own": [ "action", + "allow_file_upload", "attr", "auto_initialize", "block_name", diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.txt index aad03640e48a1..5b97c854c7b06 100644 --- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.txt +++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.txt @@ -6,6 +6,7 @@ Symfony\Component\Form\Extension\Core\Type\FormType (Block prefix: "form") Options ------------------------- action + allow_file_upload attr auto_initialize block_name diff --git a/src/Symfony/Component/HttpKernel/EventListener/TranslatorListener.php b/src/Symfony/Component/HttpKernel/EventListener/TranslatorListener.php index 7cf059886df3b..e0b344e4a8379 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/TranslatorListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/TranslatorListener.php @@ -17,8 +17,8 @@ use Symfony\Component\HttpKernel\Event\FinishRequestEvent; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; -use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; -use Symfony\Contracts\Translation\TranslatorInterface; +use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\LocaleAwareInterface; /** * Synchronizes the locale between the request and the translator. @@ -31,12 +31,12 @@ class TranslatorListener implements EventSubscriberInterface private $requestStack; /** - * @param TranslatorInterface $translator + * @param LocaleAwareInterface $translator */ public function __construct($translator, RequestStack $requestStack) { - if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { - throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); + if (!$translator instanceof TranslatorInterface && !$translator instanceof LocaleAwareInterface) { + throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, LocaleAwareInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); } $this->translator = $translator; $this->requestStack = $requestStack; diff --git a/src/Symfony/Component/HttpKernel/HttpKernel.php b/src/Symfony/Component/HttpKernel/HttpKernel.php index 25a0423854b3e..7ed4118ea4beb 100644 --- a/src/Symfony/Component/HttpKernel/HttpKernel.php +++ b/src/Symfony/Component/HttpKernel/HttpKernel.php @@ -250,6 +250,9 @@ private function handleException(\Exception $e, Request $request, int $type): Re } } + /** + * Returns a human-readable string for the specified variable. + */ private function varToString($var): string { if (\is_object($var)) { diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 225a047d141f3..d34beba4198bb 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -73,11 +73,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private $requestStackSize = 0; private $resetServices = false; - const VERSION = '4.2.0'; - const VERSION_ID = 40200; + const VERSION = '4.2.1'; + const VERSION_ID = 40201; const MAJOR_VERSION = 4; const MINOR_VERSION = 2; - const RELEASE_VERSION = 0; + const RELEASE_VERSION = 1; const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '07/2019'; diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/TranslatorListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/TranslatorListenerTest.php index c4cb9d053b93f..b448f3bfd16f1 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/TranslatorListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/TranslatorListenerTest.php @@ -17,7 +17,7 @@ use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\EventListener\TranslatorListener; use Symfony\Component\HttpKernel\HttpKernelInterface; -use Symfony\Contracts\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\LocaleAwareInterface; class TranslatorListenerTest extends TestCase { @@ -27,7 +27,7 @@ class TranslatorListenerTest extends TestCase protected function setUp() { - $this->translator = $this->getMockBuilder(TranslatorInterface::class)->getMock(); + $this->translator = $this->getMockBuilder(LocaleAwareInterface::class)->getMock(); $this->requestStack = $this->getMockBuilder('Symfony\Component\HttpFoundation\RequestStack')->getMock(); $this->listener = new TranslatorListener($this->translator, $this->requestStack); } diff --git a/src/Symfony/Component/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json index 8228377e0419b..1e5684d579901 100644 --- a/src/Symfony/Component/HttpKernel/composer.json +++ b/src/Symfony/Component/HttpKernel/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": "^7.1.3", - "symfony/contracts": "^1.0", + "symfony/contracts": "^1.0.2", "symfony/event-dispatcher": "~4.1", "symfony/http-foundation": "^4.1.1", "symfony/debug": "~3.4|~4.0", diff --git a/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php b/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php index d8a274d4a812e..574425eb457f3 100644 --- a/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php +++ b/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php @@ -638,8 +638,8 @@ public function setSymbol($attr, $value) /** * Not supported. Set a text attribute. * - * @param int $attr An attribute specifier, one of the text attribute constants - * @param int $value The attribute value + * @param int $attr An attribute specifier, one of the text attribute constants + * @param string $value The attribute value * * @return bool true on success or false on failure * diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/ConnectionOptions.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/ConnectionOptions.php index c03171eb66fff..304ba7801b5e0 100644 --- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/ConnectionOptions.php +++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/ConnectionOptions.php @@ -38,6 +38,7 @@ final class ConnectionOptions const ERROR_STRING = 0x32; const MATCHED_DN = 0x33; const DEBUG_LEVEL = 0x5001; + const TIMEOUT = 0x5002; const NETWORK_TIMEOUT = 0x5005; const X_SASL_MECH = 0x6100; const X_SASL_REALM = 0x6101; diff --git a/src/Symfony/Component/Ldap/CHANGELOG.md b/src/Symfony/Component/Ldap/CHANGELOG.md index b3f367d9ef445..7dc0c81b3dfda 100644 --- a/src/Symfony/Component/Ldap/CHANGELOG.md +++ b/src/Symfony/Component/Ldap/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG ----- * added `EntryManager::applyOperations` + * Added timeout option to `ConnectionOptions` 4.1.0 ----- diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherTrait.php b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherTrait.php index 774f503ac7f13..85c8cba8b1776 100644 --- a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherTrait.php +++ b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherTrait.php @@ -67,7 +67,7 @@ public function match($pathinfo) throw new ResourceNotFoundException(); } - private function doMatch(string $rawPathinfo, array &$allow = array(), array &$allowSchemes = array()): ?array + private function doMatch(string $rawPathinfo, array &$allow = array(), array &$allowSchemes = array()): array { $allow = $allowSchemes = array(); $pathinfo = rawurldecode($rawPathinfo) ?: '/'; @@ -90,8 +90,8 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a if ('/' === $pathinfo || $hasTrailingSlash === ('/' === $pathinfo[-1])) { // no-op - } elseif ($this instanceof RedirectableUrlMatcherInterface) { - return null; + } elseif ($this instanceof RedirectableUrlMatcherInterface && (!$requiredMethods || isset($requiredMethods['GET'])) && 'GET' === $canonicalMethod) { + return $allow = $allowSchemes = array(); } else { continue; } @@ -130,12 +130,20 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a continue; } - if ('/' === $pathinfo || (!$hasTrailingSlash ? '/' !== $pathinfo[-1] || !preg_match($regex, substr($pathinfo, 0, -1), $n) || $m !== (int) $n['MARK'] : '/' === $pathinfo[-1])) { - // no-op - } elseif ($this instanceof RedirectableUrlMatcherInterface) { - return null; - } else { - continue; + if ('/' !== $pathinfo) { + if ('/' === $pathinfo[-1]) { + if (preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { + $matches = $n; + } else { + $hasTrailingSlash = true; + } + } + if ($hasTrailingSlash !== ('/' === $pathinfo[-1])) { + if ($this instanceof RedirectableUrlMatcherInterface && (!$requiredMethods || isset($requiredMethods['GET'])) && 'GET' === $canonicalMethod) { + return $allow = $allowSchemes = array(); + } + continue; + } } foreach ($vars as $i => $v) { @@ -168,6 +176,6 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a throw new NoConfigurationException(); } - return null; + return array(); } } diff --git a/src/Symfony/Component/Routing/Matcher/UrlMatcher.php b/src/Symfony/Component/Routing/Matcher/UrlMatcher.php index 32bf4ed82d187..0d5d14da5eae3 100644 --- a/src/Symfony/Component/Routing/Matcher/UrlMatcher.php +++ b/src/Symfony/Component/Routing/Matcher/UrlMatcher.php @@ -130,21 +130,26 @@ public function addExpressionLanguageProvider(ExpressionFunctionProviderInterfac */ protected function matchCollection($pathinfo, RouteCollection $routes) { + // HEAD and GET are equivalent as per RFC + if ('HEAD' === $method = $this->context->getMethod()) { + $method = 'GET'; + } $supportsTrailingSlash = '/' !== $pathinfo && '' !== $pathinfo && $this instanceof RedirectableUrlMatcherInterface; foreach ($routes as $name => $route) { $compiledRoute = $route->compile(); $staticPrefix = $compiledRoute->getStaticPrefix(); + $requiredMethods = $route->getMethods(); // check the static prefix of the URL first. Only use the more expensive preg_match when it matches if ('' === $staticPrefix || 0 === strpos($pathinfo, $staticPrefix)) { // no-op - } elseif (!$supportsTrailingSlash) { + } elseif (!$supportsTrailingSlash || ($requiredMethods && !\in_array('GET', $requiredMethods)) || 'GET' !== $method) { continue; } elseif ('/' === $staticPrefix[-1] && substr($staticPrefix, 0, -1) === $pathinfo) { - return; + return $this->allow = $this->allowSchemes = array(); } elseif ('/' === $pathinfo[-1] && substr($pathinfo, 0, -1) === $staticPrefix) { - return; + return $this->allow = $this->allowSchemes = array(); } else { continue; } @@ -161,11 +166,18 @@ protected function matchCollection($pathinfo, RouteCollection $routes) } if ($supportsTrailingSlash) { - if (!$hasTrailingSlash && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1))) { - return; + if ('/' === $pathinfo[-1]) { + if (preg_match($regex, substr($pathinfo, 0, -1), $m)) { + $matches = $m; + } else { + $hasTrailingSlash = true; + } } - if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { - return; + if ($hasTrailingSlash !== ('/' === $pathinfo[-1])) { + if ((!$requiredMethods || \in_array('GET', $requiredMethods)) && 'GET' === $method) { + return $this->allow = $this->allowSchemes = array(); + } + continue; } } @@ -181,12 +193,7 @@ protected function matchCollection($pathinfo, RouteCollection $routes) } $hasRequiredScheme = !$route->getSchemes() || $route->hasScheme($this->context->getScheme()); - if ($requiredMethods = $route->getMethods()) { - // HEAD and GET are equivalent as per RFC - if ('HEAD' === $method = $this->context->getMethod()) { - $method = 'GET'; - } - + if ($requiredMethods) { if (!\in_array($method, $requiredMethods)) { if ($hasRequiredScheme) { $this->allow = array_merge($this->allow, $requiredMethods); @@ -204,6 +211,8 @@ protected function matchCollection($pathinfo, RouteCollection $routes) return $this->getAttributes($route, $name, array_replace($matches, $hostMatches, isset($status[1]) ? $status[1] : array())); } + + return array(); } /** diff --git a/src/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php b/src/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php index eb1703353b21e..f1d6b5325d0a5 100644 --- a/src/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php +++ b/src/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php @@ -170,6 +170,23 @@ public function testMissingTrailingSlashAndScheme() $matcher->match('/foo'); } + public function testSlashAndVerbPrecedenceWithRedirection() + { + $coll = new RouteCollection(); + $coll->add('a', new Route('/api/customers/{customerId}/contactpersons', array(), array(), array(), '', array(), array('post'))); + $coll->add('b', new Route('/api/customers/{customerId}/contactpersons/', array(), array(), array(), '', array(), array('get'))); + + $matcher = $this->getUrlMatcher($coll); + $expected = array( + '_route' => 'b', + 'customerId' => '123', + ); + $this->assertEquals($expected, $matcher->match('/api/customers/123/contactpersons/')); + + $matcher->expects($this->once())->method('redirect')->with('/api/customers/123/contactpersons/')->willReturn(array()); + $this->assertEquals($expected, $matcher->match('/api/customers/123/contactpersons')); + } + protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) { return $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($routes, $context ?: new RequestContext())); diff --git a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php index 5f24e5f794ce3..bcaf3dee7218a 100644 --- a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php +++ b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php @@ -692,6 +692,50 @@ public function testSlashVariant() $this->assertEquals('a', $matcher->match('/foo/')['_route']); } + public function testSlashVariant2() + { + $coll = new RouteCollection(); + $coll->add('a', new Route('/foo/{bar}/', array(), array('bar' => '.*'))); + + $matcher = $this->getUrlMatcher($coll); + $this->assertEquals(array('_route' => 'a', 'bar' => 'bar'), $matcher->match('/foo/bar/')); + } + + public function testSlashWithVerb() + { + $coll = new RouteCollection(); + $coll->add('a', new Route('/{foo}', array(), array(), array(), '', array(), array('put', 'delete'))); + $coll->add('b', new Route('/bar/')); + + $matcher = $this->getUrlMatcher($coll); + $this->assertSame(array('_route' => 'b'), $matcher->match('/bar/')); + } + + public function testSlashAndVerbPrecedence() + { + $coll = new RouteCollection(); + $coll->add('a', new Route('/api/customers/{customerId}/contactpersons/', array(), array(), array(), '', array(), array('post'))); + $coll->add('b', new Route('/api/customers/{customerId}/contactpersons', array(), array(), array(), '', array(), array('get'))); + + $matcher = $this->getUrlMatcher($coll); + $expected = array( + '_route' => 'b', + 'customerId' => '123', + ); + $this->assertEquals($expected, $matcher->match('/api/customers/123/contactpersons')); + + $coll = new RouteCollection(); + $coll->add('a', new Route('/api/customers/{customerId}/contactpersons/', array(), array(), array(), '', array(), array('get'))); + $coll->add('b', new Route('/api/customers/{customerId}/contactpersons', array(), array(), array(), '', array(), array('post'))); + + $matcher = $this->getUrlMatcher($coll, new RequestContext('', 'POST')); + $expected = array( + '_route' => 'b', + 'customerId' => '123', + ); + $this->assertEquals($expected, $matcher->match('/api/customers/123/contactpersons')); + } + protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) { return new UrlMatcher($routes, $context ?: new RequestContext()); diff --git a/src/Symfony/Component/Security/Http/HttpUtils.php b/src/Symfony/Component/Security/Http/HttpUtils.php index 6dbd14e986653..850e371c95429 100644 --- a/src/Symfony/Component/Security/Http/HttpUtils.php +++ b/src/Symfony/Component/Security/Http/HttpUtils.php @@ -62,10 +62,10 @@ public function __construct(UrlGeneratorInterface $urlGenerator = null, $urlMatc */ public function createRedirectResponse(Request $request, $path, $status = 302) { - if (null !== $this->secureDomainRegexp && 'https' === $this->urlMatcher->getContext()->getScheme() && preg_match('#^https?://[^/]++#i', $path, $host) && !preg_match(sprintf($this->secureDomainRegexp, preg_quote($request->getHttpHost())), $host[0])) { + if (null !== $this->secureDomainRegexp && 'https' === $this->urlMatcher->getContext()->getScheme() && preg_match('#^https?:[/\\\\]{2,}+[^/]++#i', $path, $host) && !preg_match(sprintf($this->secureDomainRegexp, preg_quote($request->getHttpHost())), $host[0])) { $path = '/'; } - if (null !== $this->domainRegexp && preg_match('#^https?://[^/]++#i', $path, $host) && !preg_match(sprintf($this->domainRegexp, preg_quote($request->getHttpHost())), $host[0])) { + if (null !== $this->domainRegexp && preg_match('#^https?:[/\\\\]{2,}+[^/]++#i', $path, $host) && !preg_match(sprintf($this->domainRegexp, preg_quote($request->getHttpHost())), $host[0])) { $path = '/'; } diff --git a/src/Symfony/Component/Security/Http/Tests/HttpUtilsTest.php b/src/Symfony/Component/Security/Http/Tests/HttpUtilsTest.php index 28b242995d4bf..a0d6f79714414 100644 --- a/src/Symfony/Component/Security/Http/Tests/HttpUtilsTest.php +++ b/src/Symfony/Component/Security/Http/Tests/HttpUtilsTest.php @@ -54,14 +54,28 @@ public function testCreateRedirectResponseWithRequestsDomain() $this->assertTrue($response->isRedirect('http://localhost/blog')); } - public function testCreateRedirectResponseWithBadRequestsDomain() + /** + * @dataProvider badRequestDomainUrls + */ + public function testCreateRedirectResponseWithBadRequestsDomain($url) { $utils = new HttpUtils($this->getUrlGenerator(), null, '#^https?://%s$#i'); - $response = $utils->createRedirectResponse($this->getRequest(), 'http://pirate.net/foo'); + $response = $utils->createRedirectResponse($this->getRequest(), $url); $this->assertTrue($response->isRedirect('http://localhost/')); } + public function badRequestDomainUrls() + { + return array( + array('http://pirate.net/foo'), + array('http:\\\\pirate.net/foo'), + array('http:/\\pirate.net/foo'), + array('http:\\/pirate.net/foo'), + array('http://////pirate.net/foo'), + ); + } + public function testCreateRedirectResponseWithProtocolRelativeTarget() { $utils = new HttpUtils($this->getUrlGenerator(), null, '#^https?://%s$#i'); diff --git a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php index 26c48e5fd49fd..02ae517f25990 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php @@ -68,7 +68,8 @@ public function normalize($object, $format = null, array $context = array()) $timezone = $this->getTimezone($context); if (null !== $timezone) { - $object = (new \DateTimeImmutable('@'.$object->getTimestamp()))->setTimezone($timezone); + $object = clone $object; + $object = $object->setTimezone($timezone); } return $object->format($dateTimeFormat); diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php index 87edb8df4c993..91b6909f17baa 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php @@ -103,6 +103,82 @@ public function normalizeUsingTimeZonePassedInContextProvider() yield array('2016-12-01T09:00:00+09:00', new \DateTimeImmutable('2016/12/01', new \DateTimeZone('UTC')), new \DateTimeZone('Japan')); } + /** + * @dataProvider normalizeUsingTimeZonePassedInContextAndExpectedFormatWithMicrosecondsProvider + */ + public function testNormalizeUsingTimeZonePassedInContextAndFormattedWithMicroseconds($expected, $expectedFormat, $input, $timezone) + { + $this->assertSame( + $expected, + $this->normalizer->normalize( + $input, + null, + array( + DateTimeNormalizer::TIMEZONE_KEY => $timezone, + DateTimeNormalizer::FORMAT_KEY => $expectedFormat, + ) + ) + ); + } + + public function normalizeUsingTimeZonePassedInContextAndExpectedFormatWithMicrosecondsProvider() + { + yield array( + '2018-12-01T18:03:06.067634', + 'Y-m-d\TH:i:s.u', + \DateTime::createFromFormat( + 'Y-m-d\TH:i:s.u', + '2018-12-01T18:03:06.067634', + new \DateTimeZone('UTC') + ), + null, + ); + + yield array( + '2018-12-01T18:03:06.067634', + 'Y-m-d\TH:i:s.u', + \DateTime::createFromFormat( + 'Y-m-d\TH:i:s.u', + '2018-12-01T18:03:06.067634', + new \DateTimeZone('UTC') + ), + new \DateTimeZone('UTC'), + ); + + yield array( + '2018-12-01T19:03:06.067634+01:00', + 'Y-m-d\TH:i:s.uP', + \DateTimeImmutable::createFromFormat( + 'Y-m-d\TH:i:s.u', + '2018-12-01T18:03:06.067634', + new \DateTimeZone('UTC') + ), + new \DateTimeZone('Europe/Rome'), + ); + + yield array( + '2018-12-01T20:03:06.067634+02:00', + 'Y-m-d\TH:i:s.uP', + \DateTime::createFromFormat( + 'Y-m-d\TH:i:s.u', + '2018-12-01T18:03:06.067634', + new \DateTimeZone('UTC') + ), + new \DateTimeZone('Europe/Kiev'), + ); + + yield array( + '2018-12-01T21:03:06.067634', + 'Y-m-d\TH:i:s.u', + \DateTime::createFromFormat( + 'Y-m-d\TH:i:s.u', + '2018-12-01T18:03:06.067634', + new \DateTimeZone('UTC') + ), + new \DateTimeZone('Europe/Moscow'), + ); + } + /** * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException * @expectedExceptionMessage The object must implement the "\DateTimeInterface". diff --git a/src/Symfony/Component/Translation/DataCollectorTranslator.php b/src/Symfony/Component/Translation/DataCollectorTranslator.php index 5e547163a3a03..ca6bedc71bfa6 100644 --- a/src/Symfony/Component/Translation/DataCollectorTranslator.php +++ b/src/Symfony/Component/Translation/DataCollectorTranslator.php @@ -13,6 +13,7 @@ use Symfony\Component\Translation\Exception\InvalidArgumentException; use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; +use Symfony\Contracts\Translation\LocaleAwareInterface; use Symfony\Contracts\Translation\TranslatorInterface; /** @@ -39,8 +40,8 @@ public function __construct($translator) if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); } - if (!$translator instanceof TranslatorBagInterface) { - throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface and TranslatorBagInterface.', \get_class($translator))); + if (!$translator instanceof TranslatorBagInterface || !$translator instanceof LocaleAwareInterface) { + throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface, TranslatorBagInterface and LocaleAwareInterface.', \get_class($translator))); } $this->translator = $translator; diff --git a/src/Symfony/Component/Translation/Formatter/MessageFormatter.php b/src/Symfony/Component/Translation/Formatter/MessageFormatter.php index 11f766c8cd216..e36f242c89e42 100644 --- a/src/Symfony/Component/Translation/Formatter/MessageFormatter.php +++ b/src/Symfony/Component/Translation/Formatter/MessageFormatter.php @@ -66,7 +66,7 @@ public function formatIntl(string $message, string $locale, array $parameters = */ public function choiceFormat($message, $number, $locale, array $parameters = array()) { - @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2, use the format() one instead with a %count% parameter.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2, use the format() one instead with a %%count%% parameter.', __METHOD__), E_USER_DEPRECATED); $parameters = array('%count%' => $number) + $parameters; diff --git a/src/Symfony/Component/Translation/LoggingTranslator.php b/src/Symfony/Component/Translation/LoggingTranslator.php index 2746fcfe3007c..4fe756d7a7fbc 100644 --- a/src/Symfony/Component/Translation/LoggingTranslator.php +++ b/src/Symfony/Component/Translation/LoggingTranslator.php @@ -14,6 +14,7 @@ use Psr\Log\LoggerInterface; use Symfony\Component\Translation\Exception\InvalidArgumentException; use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; +use Symfony\Contracts\Translation\LocaleAwareInterface; use Symfony\Contracts\Translation\TranslatorInterface; /** @@ -37,8 +38,8 @@ public function __construct($translator, LoggerInterface $logger) if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); } - if (!$translator instanceof TranslatorBagInterface) { - throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface and TranslatorBagInterface.', \get_class($translator))); + if (!$translator instanceof TranslatorBagInterface || !$translator instanceof LocaleAwareInterface) { + throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface, TranslatorBagInterface and LocaleAwareInterface.', \get_class($translator))); } $this->translator = $translator; diff --git a/src/Symfony/Component/Translation/TranslatorInterface.php b/src/Symfony/Component/Translation/TranslatorInterface.php index 02cb5027b862b..3ecef054b1cee 100644 --- a/src/Symfony/Component/Translation/TranslatorInterface.php +++ b/src/Symfony/Component/Translation/TranslatorInterface.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Translation; use Symfony\Component\Translation\Exception\InvalidArgumentException; +use Symfony\Contracts\Translation\LocaleAwareInterface; /** * TranslatorInterface. @@ -20,7 +21,7 @@ * * @deprecated since Symfony 4.2, use Symfony\Contracts\Translation\TranslatorInterface instead */ -interface TranslatorInterface +interface TranslatorInterface extends LocaleAwareInterface { /** * Translates the given message. diff --git a/src/Symfony/Component/Translation/composer.json b/src/Symfony/Component/Translation/composer.json index 29ba62ab15eab..7d5df73025304 100644 --- a/src/Symfony/Component/Translation/composer.json +++ b/src/Symfony/Component/Translation/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": "^7.1.3", - "symfony/contracts": "^1.0", + "symfony/contracts": "^1.0.2", "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { diff --git a/src/Symfony/Component/Validator/ConstraintViolation.php b/src/Symfony/Component/Validator/ConstraintViolation.php index 57ece5ba37259..8651913c3e7f3 100644 --- a/src/Symfony/Component/Validator/ConstraintViolation.php +++ b/src/Symfony/Component/Validator/ConstraintViolation.php @@ -79,13 +79,13 @@ public function __toString() } $propertyPath = (string) $this->propertyPath; - $code = $this->code; + $code = (string) $this->code; if ('' !== $propertyPath && '[' !== $propertyPath[0] && '' !== $class) { $class .= '.'; } - if (!empty($code)) { + if ('' !== $code) { $code = ' (code '.$code.')'; } diff --git a/src/Symfony/Component/Validator/Tests/ConstraintViolationTest.php b/src/Symfony/Component/Validator/Tests/ConstraintViolationTest.php index cef4782e0f82d..edaa7fa50d6b0 100644 --- a/src/Symfony/Component/Validator/Tests/ConstraintViolationTest.php +++ b/src/Symfony/Component/Validator/Tests/ConstraintViolationTest.php @@ -53,4 +53,59 @@ public function testToStringHandlesArrayRoots() $this->assertSame($expected, (string) $violation); } + + public function testToStringHandlesCodes() + { + $violation = new ConstraintViolation( + '42 cannot be used here', + 'this is the message template', + array(), + array('some_value' => 42), + 'some_value', + null, + null, + 0 + ); + + $expected = <<<'EOF' +Array.some_value: + 42 cannot be used here (code 0) +EOF; + + $this->assertSame($expected, (string) $violation); + } + + public function testToStringOmitsEmptyCodes() + { + $expected = <<<'EOF' +Array.some_value: + 42 cannot be used here +EOF; + + $violation = new ConstraintViolation( + '42 cannot be used here', + 'this is the message template', + array(), + array('some_value' => 42), + 'some_value', + null, + null, + null + ); + + $this->assertSame($expected, (string) $violation); + + $violation = new ConstraintViolation( + '42 cannot be used here', + 'this is the message template', + array(), + array('some_value' => 42), + 'some_value', + null, + null, + '' + ); + + $this->assertSame($expected, (string) $violation); + } } diff --git a/src/Symfony/Component/Validator/Tests/DependencyInjection/AddValidatorInitializersPassTest.php b/src/Symfony/Component/Validator/Tests/DependencyInjection/AddValidatorInitializersPassTest.php index 26d5e6646d541..ff7e28a29e576 100644 --- a/src/Symfony/Component/Validator/Tests/DependencyInjection/AddValidatorInitializersPassTest.php +++ b/src/Symfony/Component/Validator/Tests/DependencyInjection/AddValidatorInitializersPassTest.php @@ -17,6 +17,7 @@ use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\Validator\DependencyInjection\AddValidatorInitializersPass; use Symfony\Component\Validator\Util\LegacyTranslatorProxy; +use Symfony\Contracts\Translation\LocaleAwareInterface; use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorTrait; @@ -72,7 +73,7 @@ public function testLegacyTranslatorProxy() } } -class TestTranslator implements TranslatorInterface +class TestTranslator implements TranslatorInterface, LocaleAwareInterface { use TranslatorTrait; } diff --git a/src/Symfony/Component/Validator/Util/LegacyTranslatorProxy.php b/src/Symfony/Component/Validator/Util/LegacyTranslatorProxy.php index 12f487bf2f896..f87c1fbbb3eae 100644 --- a/src/Symfony/Component/Validator/Util/LegacyTranslatorProxy.php +++ b/src/Symfony/Component/Validator/Util/LegacyTranslatorProxy.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Validator\Util; use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; +use Symfony\Contracts\Translation\LocaleAwareInterface; use Symfony\Contracts\Translation\TranslatorInterface; /** @@ -23,6 +24,9 @@ class LegacyTranslatorProxy implements LegacyTranslatorInterface, TranslatorInte public function __construct(TranslatorInterface $translator) { + if (!$translator instanceof LocaleAwareInterface) { + throw new \InvalidArgumentException(sprintf('The translator passed to "%s()" must implement "%s".', __METHOD__, LocaleAwareInterface::class)); + } $this->translator = $translator; } diff --git a/src/Symfony/Component/Validator/ValidatorBuilder.php b/src/Symfony/Component/Validator/ValidatorBuilder.php index 7a4b52164aec0..8e4de01267e04 100644 --- a/src/Symfony/Component/Validator/ValidatorBuilder.php +++ b/src/Symfony/Component/Validator/ValidatorBuilder.php @@ -30,6 +30,7 @@ use Symfony\Component\Validator\Mapping\Loader\YamlFileLoader; use Symfony\Component\Validator\Util\LegacyTranslatorProxy; use Symfony\Component\Validator\Validator\RecursiveValidator; +use Symfony\Contracts\Translation\LocaleAwareInterface; use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorTrait; @@ -332,7 +333,7 @@ public function getValidator() $translator = $this->translator; if (null === $translator) { - $translator = new class() implements TranslatorInterface { + $translator = new class() implements TranslatorInterface, LocaleAwareInterface { use TranslatorTrait; }; // Force the locale to be 'en' when no translator is provided rather than relying on the Intl default locale diff --git a/src/Symfony/Component/Validator/composer.json b/src/Symfony/Component/Validator/composer.json index 442d7c67b4411..88a29241f57c7 100644 --- a/src/Symfony/Component/Validator/composer.json +++ b/src/Symfony/Component/Validator/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": "^7.1.3", - "symfony/contracts": "^1.0", + "symfony/contracts": "^1.0.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.0" }, diff --git a/src/Symfony/Component/VarExporter/Internal/Hydrator.php b/src/Symfony/Component/VarExporter/Internal/Hydrator.php index 0de27743d98dd..a1a60112d9a92 100644 --- a/src/Symfony/Component/VarExporter/Internal/Hydrator.php +++ b/src/Symfony/Component/VarExporter/Internal/Hydrator.php @@ -11,6 +11,8 @@ namespace Symfony\Component\VarExporter\Internal; +use Symfony\Component\VarExporter\Exception\ClassNotFoundException; + /** * @author Nicolas Grekas * @@ -59,7 +61,10 @@ public static function getHydrator($class) }; } - $classReflector = Registry::$reflectors[$class] ?? Registry::getClassReflector($class); + if (!\class_exists($class) && !\interface_exists($class, false) && !\trait_exists($class, false)) { + throw new ClassNotFoundException($class); + } + $classReflector = new \ReflectionClass($class); if (!$classReflector->isInternal()) { return self::$hydrators[$class] = (self::$hydrators['stdClass'] ?? self::getHydrator('stdClass'))->bindTo(null, $class); diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/abstract-parent.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/abstract-parent.php new file mode 100644 index 0000000000000..df6a067087609 --- /dev/null +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/abstract-parent.php @@ -0,0 +1,20 @@ + [ + 'foo' => [ + 123, + ], + 'bar' => [ + 234, + ], + ], + ], + $o[0], + [] +); diff --git a/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php index f5096176868ad..ea0b6a45851d3 100644 --- a/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php +++ b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php @@ -192,6 +192,8 @@ public function provideExport() $value->bis = new \ReflectionClass($value); yield array('wakeup-refl', $value); + + yield array('abstract-parent', new ConcreteClass()); } } @@ -320,3 +322,23 @@ public function __clone() throw new \BadMethodCallException('Should not be called.'); } } + +abstract class AbstractClass +{ + protected $foo; + private $bar; + + protected function setBar($bar) + { + $this->bar = $bar; + } +} + +class ConcreteClass extends AbstractClass +{ + public function __construct() + { + $this->foo = 123; + $this->setBar(234); + } +} diff --git a/src/Symfony/Contracts/Translation/LocaleAwareInterface.php b/src/Symfony/Contracts/Translation/LocaleAwareInterface.php new file mode 100644 index 0000000000000..dbd8894fe7899 --- /dev/null +++ b/src/Symfony/Contracts/Translation/LocaleAwareInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Translation; + +interface LocaleAwareInterface +{ + /** + * Sets the current locale. + * + * @param string $locale The locale + * + * @throws \InvalidArgumentException If the locale contains invalid characters + */ + public function setLocale($locale); + + /** + * Returns the current locale. + * + * @return string The locale + */ + public function getLocale(); +} diff --git a/src/Symfony/Contracts/Translation/TranslatorInterface.php b/src/Symfony/Contracts/Translation/TranslatorInterface.php index 2130c1b2cf0a6..2bdc415cbdcf2 100644 --- a/src/Symfony/Contracts/Translation/TranslatorInterface.php +++ b/src/Symfony/Contracts/Translation/TranslatorInterface.php @@ -62,20 +62,4 @@ interface TranslatorInterface * @throws \InvalidArgumentException If the locale contains invalid characters */ public function trans($id, array $parameters = array(), $domain = null, $locale = null); - - /** - * Sets the current locale. - * - * @param string $locale The locale - * - * @throws \InvalidArgumentException If the locale contains invalid characters - */ - public function setLocale($locale); - - /** - * Returns the current locale. - * - * @return string The locale - */ - public function getLocale(); } diff --git a/src/Symfony/Contracts/Translation/TranslatorTrait.php b/src/Symfony/Contracts/Translation/TranslatorTrait.php index e19e37cfe5208..4e53fbd42c4c8 100644 --- a/src/Symfony/Contracts/Translation/TranslatorTrait.php +++ b/src/Symfony/Contracts/Translation/TranslatorTrait.php @@ -14,7 +14,7 @@ use Symfony\Component\Translation\Exception\InvalidArgumentException; /** - * A trait to help implement TranslatorInterface. + * A trait to help implement TranslatorInterface and LocaleAwareInterface. * * @author Fabien Potencier */ pFad - Phonifier reborn

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

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


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy