diff --git a/UPGRADE-4.4.md b/UPGRADE-4.4.md new file mode 100644 index 0000000000000..96c170675cf6d --- /dev/null +++ b/UPGRADE-4.4.md @@ -0,0 +1,94 @@ +UPGRADE FROM 4.3 to 4.4 +======================= + +DependencyInjection +------------------- + + * Deprecated support for short factories and short configurators in Yaml + + Before: + ```yaml + services: + my_service: + factory: factory_service:method + ``` + + After: + ```yaml + services: + my_service: + factory: ['@factory_service', method] + ``` + * Deprecated `tagged` in favor of `tagged_iterator` + + Before: + ```yaml + services: + App\Handler: + tags: ['app.handler'] + + App\HandlerCollection: + arguments: [!tagged app.handler] + ``` + + After: + ```yaml + services: + App\Handler: + tags: ['app.handler'] + + App\HandlerCollection: + arguments: [!tagged_iterator app.handler] + ``` + +Form +---- + + * Using `int` or `float` as data for the `NumberType` when the `input` option is set to `string` is deprecated. + +FrameworkBundle +--------------- + + * Deprecated support for `templating` engine in `TemplateController`, use Twig instead + * The `$parser` argument of `ControllerResolver::__construct()` and `DelegatingLoader::__construct()` + has been deprecated. + * The `ControllerResolver` and `DelegatingLoader` classes have been marked as `final`. + * The `controller_name_converter` and `resolve_controller_name_subscriber` services have been deprecated. + +HttpClient +---------- + + * Added method `cancel()` to `ResponseInterface` + +HttpKernel +---------- + + * The `DebugHandlersListener` class has been marked as `final` + +Messenger +--------- + + * Deprecated passing a `ContainerInterface` instance as first argument of the `ConsumeMessagesCommand` constructor, + pass a `RoutableMessageBus` instance instead. + +MonologBridge +-------------- + + * The `RouteProcessor` has been marked final. + +Security +-------- + + * Implementations of `PasswordEncoderInterface` and `UserPasswordEncoderInterface` should add a new `needsRehash()` method + +TwigBridge +---------- + + * Deprecated to pass `$rootDir` and `$fileLinkFormatter` as 5th and 6th argument respectively to the + `DebugCommand::__construct()` method, swap the variables position. + +Validator +--------- + + * Deprecated passing an `ExpressionLanguage` instance as the second argument of `ExpressionValidator::__construct()`. + Pass it as the first argument instead. diff --git a/UPGRADE-5.0.md b/UPGRADE-5.0.md index b753931008327..442572c4684d5 100644 --- a/UPGRADE-5.0.md +++ b/UPGRADE-5.0.md @@ -69,6 +69,43 @@ DependencyInjection env(NAME): '1.5' ``` + * Removed support for short factories and short configurators in Yaml + + Before: + ```yaml + services: + my_service: + factory: factory_service:method + ``` + + After: + ```yaml + services: + my_service: + factory: ['@factory_service', method] + ``` + * Removed `tagged` in favor of `tagged_iterator` + + Before: + ```yaml + services: + App\Handler: + tags: ['app.handler'] + + App\HandlerCollection: + arguments: [!tagged app.handler] + ``` + + After: + ```yaml + services: + App\Handler: + tags: ['app.handler'] + + App\HandlerCollection: + arguments: [!tagged_iterator app.handler] + ``` + DoctrineBridge -------------- @@ -109,6 +146,7 @@ Finder Form ---- + * Removed support for using `int` or `float` as data for the `NumberType` when the `input` option is set to `string`. * Removed support for using the `format` option of `DateType` and `DateTimeType` when the `html5` option is enabled. * Using names for buttons that do not start with a letter, a digit, or an underscore leads to an exception. * Using names for buttons that do not contain only letters, digits, underscores, hyphens, and colons leads to an @@ -203,6 +241,16 @@ FrameworkBundle * Removed support for legacy translations directories `src/Resources/translations/` and `src/Resources//translations/`, use `translations/` instead. * Support for the legacy directory structure in `translation:update` and `debug:translation` commands has been removed. * Removed the "Psr\SimpleCache\CacheInterface" / "cache.app.simple" service, use "Symfony\Contracts\Cache\CacheInterface" / "cache.app" instead. + * Removed support for `templating` engine in `TemplateController`, use Twig instead + +HttpClient +---------- + + * Added method `cancel()` to `ResponseInterface` + * The `$parser` argument of `ControllerResolver::__construct()` and `DelegatingLoader::__construct()` + has been removed. + * The `ControllerResolver` and `DelegatingLoader` classes have been made `final`. + * The `controller_name_converter` and `resolve_controller_name_subscriber` services have been removed. HttpFoundation -------------- @@ -238,6 +286,7 @@ HttpKernel * Removed `GetResponseForExceptionEvent`, use `ExceptionEvent` instead * Removed `PostResponseEvent`, use `TerminateEvent` instead * Removed `TranslatorListener` in favor of `LocaleAwareListener` + * The `DebugHandlersListener` class has been made `final` Intl ---- @@ -252,12 +301,19 @@ Messenger --------- * The `LoggingMiddleware` class has been removed, pass a logger to `SendMessageMiddleware` instead. + * Passing a `ContainerInterface` instance as first argument of the `ConsumeMessagesCommand` constructor now + throws as `\TypeError`, pass a `RoutableMessageBus` instance instead. Monolog ------- * The methods `DebugProcessor::getLogs()`, `DebugProcessor::countErrors()`, `Logger::getLogs()` and `Logger::countErrors()` have a new `$request` argument. +MonologBridge +-------------- + +* The `RouteProcessor` class is final. + Process ------- @@ -289,6 +345,7 @@ Routing Security -------- + * Implementations of `PasswordEncoderInterface` and `UserPasswordEncoderInterface` must have a new `needsRehash()` method * The `Role` and `SwitchUserRole` classes have been removed. * The `getReachableRoles()` method of the `RoleHierarchy` class has been removed. It has been replaced by the new `getReachableRoleNames()` method. @@ -389,6 +446,7 @@ TwigBundle TwigBridge ---------- + * Removed argument `$rootDir` from the `DebugCommand::__construct()` method and the 5th argument must be an instance of `FileLinkFormatter` * removed the `$requestStack` and `$requestContext` arguments of the `HttpFoundationExtension`, pass a `Symfony\Component\HttpFoundation\UrlHelper` instance as the only argument instead @@ -396,6 +454,7 @@ TwigBridge Validator -------- + * An `ExpressionLanguage` instance or null must be passed as the first argument of `ExpressionValidator::__construct()` * The `checkMX` and `checkHost` options of the `Email` constraint were removed * The `Email::__construct()` 'strict' property has been removed. Use 'mode'=>"strict" instead. * Calling `EmailValidator::__construct()` method with a boolean parameter has been removed, use `EmailValidator("strict")` instead. diff --git a/composer.json b/composer.json index 206141f8a7382..955e6670dc5b1 100644 --- a/composer.json +++ b/composer.json @@ -111,6 +111,7 @@ "monolog/monolog": "~1.11", "nyholm/psr7": "^1.0", "ocramius/proxy-manager": "^2.1", + "php-http/httplug": "^1.0|^2.0", "predis/predis": "~1.1", "psr/http-client": "^1.0", "psr/simple-cache": "^1.0", @@ -154,7 +155,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Bridge/Doctrine/CHANGELOG.md b/src/Symfony/Bridge/Doctrine/CHANGELOG.md index b9baff3763c6c..7123b527ebc3d 100644 --- a/src/Symfony/Bridge/Doctrine/CHANGELOG.md +++ b/src/Symfony/Bridge/Doctrine/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +4.4.0 +----- + + * added `DoctrineClearEntityManagerMiddleware` + + 4.3.0 ----- diff --git a/src/Symfony/Bridge/Doctrine/Messenger/AbstractDoctrineMiddleware.php b/src/Symfony/Bridge/Doctrine/Messenger/AbstractDoctrineMiddleware.php new file mode 100644 index 0000000000000..a29c95af3da03 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Messenger/AbstractDoctrineMiddleware.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Messenger; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Exception\UnrecoverableMessageHandlingException; +use Symfony\Component\Messenger\Middleware\MiddlewareInterface; +use Symfony\Component\Messenger\Middleware\StackInterface; + +/** + * @author Konstantin Myakshin + * + * @internal + */ +abstract class AbstractDoctrineMiddleware implements MiddlewareInterface +{ + protected $managerRegistry; + protected $entityManagerName; + + public function __construct(ManagerRegistry $managerRegistry, string $entityManagerName = null) + { + $this->managerRegistry = $managerRegistry; + $this->entityManagerName = $entityManagerName; + } + + final public function handle(Envelope $envelope, StackInterface $stack): Envelope + { + try { + $entityManager = $this->managerRegistry->getManager($this->entityManagerName); + } catch (\InvalidArgumentException $e) { + throw new UnrecoverableMessageHandlingException($e->getMessage(), 0, $e); + } + + return $this->handleForManager($entityManager, $envelope, $stack); + } + + abstract protected function handleForManager(EntityManagerInterface $entityManager, Envelope $envelope, StackInterface $stack): Envelope; +} diff --git a/src/Symfony/Bridge/Doctrine/Messenger/DoctrineClearEntityManagerMiddleware.php b/src/Symfony/Bridge/Doctrine/Messenger/DoctrineClearEntityManagerMiddleware.php new file mode 100644 index 0000000000000..bb0782232ee38 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Messenger/DoctrineClearEntityManagerMiddleware.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Messenger; + +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Middleware\StackInterface; + +/** + * Clears entity manager after calling all handlers. + * + * @author Konstantin Myakshin + */ +class DoctrineClearEntityManagerMiddleware extends AbstractDoctrineMiddleware +{ + protected function handleForManager(EntityManagerInterface $entityManager, Envelope $envelope, StackInterface $stack): Envelope + { + try { + return $stack->next()->handle($envelope, $stack); + } finally { + $entityManager->clear(); + } + } +} diff --git a/src/Symfony/Bridge/Doctrine/Messenger/DoctrineCloseConnectionMiddleware.php b/src/Symfony/Bridge/Doctrine/Messenger/DoctrineCloseConnectionMiddleware.php index 0996859221d22..2a7395ad60093 100644 --- a/src/Symfony/Bridge/Doctrine/Messenger/DoctrineCloseConnectionMiddleware.php +++ b/src/Symfony/Bridge/Doctrine/Messenger/DoctrineCloseConnectionMiddleware.php @@ -11,10 +11,8 @@ namespace Symfony\Bridge\Doctrine\Messenger; -use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Messenger\Envelope; -use Symfony\Component\Messenger\Exception\UnrecoverableMessageHandlingException; -use Symfony\Component\Messenger\Middleware\MiddlewareInterface; use Symfony\Component\Messenger\Middleware\StackInterface; /** @@ -24,28 +22,10 @@ * * @experimental in 4.3 */ -class DoctrineCloseConnectionMiddleware implements MiddlewareInterface +class DoctrineCloseConnectionMiddleware extends AbstractDoctrineMiddleware { - private $managerRegistry; - private $entityManagerName; - - public function __construct(ManagerRegistry $managerRegistry, string $entityManagerName = null) - { - $this->managerRegistry = $managerRegistry; - $this->entityManagerName = $entityManagerName; - } - - /** - * {@inheritdoc} - */ - public function handle(Envelope $envelope, StackInterface $stack): Envelope + protected function handleForManager(EntityManagerInterface $entityManager, Envelope $envelope, StackInterface $stack): Envelope { - try { - $entityManager = $this->managerRegistry->getManager($this->entityManagerName); - } catch (\InvalidArgumentException $e) { - throw new UnrecoverableMessageHandlingException($e->getMessage(), 0, $e); - } - try { $connection = $entityManager->getConnection(); diff --git a/src/Symfony/Bridge/Doctrine/Messenger/DoctrinePingConnectionMiddleware.php b/src/Symfony/Bridge/Doctrine/Messenger/DoctrinePingConnectionMiddleware.php index ca9f65d9debf0..98cbc8ed410a9 100644 --- a/src/Symfony/Bridge/Doctrine/Messenger/DoctrinePingConnectionMiddleware.php +++ b/src/Symfony/Bridge/Doctrine/Messenger/DoctrinePingConnectionMiddleware.php @@ -11,10 +11,8 @@ namespace Symfony\Bridge\Doctrine\Messenger; -use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Messenger\Envelope; -use Symfony\Component\Messenger\Exception\UnrecoverableMessageHandlingException; -use Symfony\Component\Messenger\Middleware\MiddlewareInterface; use Symfony\Component\Messenger\Middleware\StackInterface; /** @@ -24,28 +22,10 @@ * * @experimental in 4.3 */ -class DoctrinePingConnectionMiddleware implements MiddlewareInterface +class DoctrinePingConnectionMiddleware extends AbstractDoctrineMiddleware { - private $managerRegistry; - private $entityManagerName; - - public function __construct(ManagerRegistry $managerRegistry, string $entityManagerName = null) - { - $this->managerRegistry = $managerRegistry; - $this->entityManagerName = $entityManagerName; - } - - /** - * {@inheritdoc} - */ - public function handle(Envelope $envelope, StackInterface $stack): Envelope + protected function handleForManager(EntityManagerInterface $entityManager, Envelope $envelope, StackInterface $stack): Envelope { - try { - $entityManager = $this->managerRegistry->getManager($this->entityManagerName); - } catch (\InvalidArgumentException $e) { - throw new UnrecoverableMessageHandlingException($e->getMessage(), 0, $e); - } - $connection = $entityManager->getConnection(); if (!$connection->ping()) { diff --git a/src/Symfony/Bridge/Doctrine/Messenger/DoctrineTransactionMiddleware.php b/src/Symfony/Bridge/Doctrine/Messenger/DoctrineTransactionMiddleware.php index 62f0bac51dd7b..b0ff88410d29f 100644 --- a/src/Symfony/Bridge/Doctrine/Messenger/DoctrineTransactionMiddleware.php +++ b/src/Symfony/Bridge/Doctrine/Messenger/DoctrineTransactionMiddleware.php @@ -11,11 +11,9 @@ namespace Symfony\Bridge\Doctrine\Messenger; -use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Exception\HandlerFailedException; -use Symfony\Component\Messenger\Exception\UnrecoverableMessageHandlingException; -use Symfony\Component\Messenger\Middleware\MiddlewareInterface; use Symfony\Component\Messenger\Middleware\StackInterface; use Symfony\Component\Messenger\Stamp\HandledStamp; @@ -26,28 +24,10 @@ * * @experimental in 4.3 */ -class DoctrineTransactionMiddleware implements MiddlewareInterface +class DoctrineTransactionMiddleware extends AbstractDoctrineMiddleware { - private $managerRegistry; - private $entityManagerName; - - public function __construct(ManagerRegistry $managerRegistry, string $entityManagerName = null) - { - $this->managerRegistry = $managerRegistry; - $this->entityManagerName = $entityManagerName; - } - - /** - * {@inheritdoc} - */ - public function handle(Envelope $envelope, StackInterface $stack): Envelope + protected function handleForManager(EntityManagerInterface $entityManager, Envelope $envelope, StackInterface $stack): Envelope { - try { - $entityManager = $this->managerRegistry->getManager($this->entityManagerName); - } catch (\InvalidArgumentException $e) { - throw new UnrecoverableMessageHandlingException($e->getMessage(), 0, $e); - } - $entityManager->getConnection()->beginTransaction(); try { $envelope = $stack->next()->handle($envelope, $stack); diff --git a/src/Symfony/Bridge/Doctrine/Tests/Messenger/DoctrineClearEntityManagerMiddlewareTest.php b/src/Symfony/Bridge/Doctrine/Tests/Messenger/DoctrineClearEntityManagerMiddlewareTest.php new file mode 100644 index 0000000000000..d20c9cfb50690 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Messenger/DoctrineClearEntityManagerMiddlewareTest.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Messenger; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Bridge\Doctrine\Messenger\DoctrineClearEntityManagerMiddleware; +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Exception\UnrecoverableMessageHandlingException; +use Symfony\Component\Messenger\Test\Middleware\MiddlewareTestCase; + +class DoctrineClearEntityManagerMiddlewareTest extends MiddlewareTestCase +{ + public function testMiddlewareClearEntityManager() + { + $entityManager = $this->createMock(EntityManagerInterface::class); + $entityManager->expects($this->once()) + ->method('clear'); + + $managerRegistry = $this->createMock(ManagerRegistry::class); + $managerRegistry + ->method('getManager') + ->with('default') + ->willReturn($entityManager); + + $middleware = new DoctrineClearEntityManagerMiddleware($managerRegistry, 'default'); + + $middleware->handle(new Envelope(new \stdClass()), $this->getStackMock()); + } + + public function testInvalidEntityManagerThrowsException() + { + $managerRegistry = $this->createMock(ManagerRegistry::class); + $managerRegistry + ->method('getManager') + ->with('unknown_manager') + ->will($this->throwException(new \InvalidArgumentException())); + + $middleware = new DoctrineClearEntityManagerMiddleware($managerRegistry, 'unknown_manager'); + + $this->expectException(UnrecoverableMessageHandlingException::class); + + $middleware->handle(new Envelope(new \stdClass()), $this->getStackMock(false)); + } +} diff --git a/src/Symfony/Bridge/Doctrine/composer.json b/src/Symfony/Bridge/Doctrine/composer.json index b00634b0de1a0..d022f57c32de2 100644 --- a/src/Symfony/Bridge/Doctrine/composer.json +++ b/src/Symfony/Bridge/Doctrine/composer.json @@ -24,19 +24,19 @@ "symfony/service-contracts": "^1.1" }, "require-dev": { - "symfony/stopwatch": "~3.4|~4.0", - "symfony/config": "^4.2", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/form": "~4.3", - "symfony/http-kernel": "~3.4|~4.0", - "symfony/messenger": "~4.3", - "symfony/property-access": "~3.4|~4.0", - "symfony/property-info": "~3.4|~4.0", - "symfony/proxy-manager-bridge": "~3.4|~4.0", - "symfony/security-core": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/validator": "~3.4|~4.0", - "symfony/translation": "~3.4|~4.0", + "symfony/stopwatch": "^3.4|^4.0|^5.0", + "symfony/config": "^4.2|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/form": "^4.3|^5.0", + "symfony/http-kernel": "^3.4|^4.0|^5.0", + "symfony/messenger": "^4.3|^5.0", + "symfony/property-access": "^3.4|^4.0|^5.0", + "symfony/property-info": "^3.4|^4.0|^5.0", + "symfony/proxy-manager-bridge": "^3.4|^4.0|^5.0", + "symfony/security-core": "^3.4|^4.0|^5.0", + "symfony/expression-language": "^3.4|^4.0|^5.0", + "symfony/validator": "^3.4|^4.0|^5.0", + "symfony/translation": "^3.4|^4.0|^5.0", "doctrine/annotations": "~1.0", "doctrine/cache": "~1.6", "doctrine/collections": "~1.0", @@ -68,7 +68,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Bridge/Monolog/CHANGELOG.md b/src/Symfony/Bridge/Monolog/CHANGELOG.md index 8b519c9f31104..20f0dc788b709 100644 --- a/src/Symfony/Bridge/Monolog/CHANGELOG.md +++ b/src/Symfony/Bridge/Monolog/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +4.4.0 +----- + +* The `RouteProcessor` class has been made final + 4.3.0 ----- diff --git a/src/Symfony/Bridge/Monolog/Processor/RouteProcessor.php b/src/Symfony/Bridge/Monolog/Processor/RouteProcessor.php index 0160754ad9575..09507b55e7fb2 100644 --- a/src/Symfony/Bridge/Monolog/Processor/RouteProcessor.php +++ b/src/Symfony/Bridge/Monolog/Processor/RouteProcessor.php @@ -21,6 +21,8 @@ * Adds the current route information to the log entry. * * @author Piotr Stankowski + * + * @final since Symfony 4.4 */ class RouteProcessor implements EventSubscriberInterface, ResetInterface { diff --git a/src/Symfony/Bridge/Monolog/composer.json b/src/Symfony/Bridge/Monolog/composer.json index e57ae259e9d78..846d427b756c1 100644 --- a/src/Symfony/Bridge/Monolog/composer.json +++ b/src/Symfony/Bridge/Monolog/composer.json @@ -22,9 +22,9 @@ "symfony/http-kernel": "^4.3" }, "require-dev": { - "symfony/console": "~3.4|~4.0", - "symfony/security-core": "~3.4|~4.0", - "symfony/var-dumper": "~3.4|~4.0" + "symfony/console": "^3.4|^4.0|^5.0", + "symfony/security-core": "^3.4|^4.0|^5.0", + "symfony/var-dumper": "^3.4|^4.0|^5.0" }, "conflict": { "symfony/console": "<3.4", @@ -44,7 +44,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php index c11816ba543c0..c9d2bf50fee5a 100644 --- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php +++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php @@ -47,9 +47,12 @@ return $default; }; -if (PHP_VERSION_ID >= 70100) { +if (PHP_VERSION_ID >= 70200) { + // PHPUnit 8 requires PHP 7.2+ + $PHPUNIT_VERSION = $getEnvVar('SYMFONY_PHPUNIT_VERSION', '8.2'); +} elseif (PHP_VERSION_ID >= 70100) { // PHPUnit 7 requires PHP 7.1+ - $PHPUNIT_VERSION = $getEnvVar('SYMFONY_PHPUNIT_VERSION', '7.4'); + $PHPUNIT_VERSION = $getEnvVar('SYMFONY_PHPUNIT_VERSION', '7.5'); } elseif (PHP_VERSION_ID >= 70000) { // PHPUnit 6 requires PHP 7.0+ $PHPUNIT_VERSION = $getEnvVar('SYMFONY_PHPUNIT_VERSION', '6.5'); @@ -128,7 +131,7 @@ $prevRoot = getenv('COMPOSER_ROOT_VERSION'); putenv("COMPOSER_ROOT_VERSION=$PHPUNIT_VERSION.99"); // --no-suggest is not in the list to keep compat with composer 1.0, which is shipped with Ubuntu 16.04LTS - $exit = proc_close(proc_open("$COMPOSER install --no-dev --prefer-dist --no-progress --ansi", array(), $p, getcwd(), null, array('bypass_shell' => true))); + $exit = proc_close(proc_open("$COMPOSER install --no-dev --prefer-dist --no-progress --ansi", array(), $p, getcwd(), null, array('bypass_shell' => false))); putenv('COMPOSER_ROOT_VERSION'.(false !== $prevRoot ? '='.$prevRoot : '')); if ($exit) { exit($exit); diff --git a/src/Symfony/Bridge/PhpUnit/composer.json b/src/Symfony/Bridge/PhpUnit/composer.json index 496bb8a385039..1518be6503b55 100644 --- a/src/Symfony/Bridge/PhpUnit/composer.json +++ b/src/Symfony/Bridge/PhpUnit/composer.json @@ -39,7 +39,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" }, "thanks": { "name": "phpunit/phpunit", diff --git a/src/Symfony/Bridge/ProxyManager/composer.json b/src/Symfony/Bridge/ProxyManager/composer.json index d5ce7a3e3989f..c7038a9c19d48 100644 --- a/src/Symfony/Bridge/ProxyManager/composer.json +++ b/src/Symfony/Bridge/ProxyManager/composer.json @@ -17,11 +17,11 @@ ], "require": { "php": "^7.1.3", - "symfony/dependency-injection": "~4.0", + "symfony/dependency-injection": "^4.0|^5.0", "ocramius/proxy-manager": "~2.1" }, "require-dev": { - "symfony/config": "~3.4|~4.0" + "symfony/config": "^3.4|^4.0|^5.0" }, "conflict": { "zendframework/zend-eventmanager": "2.6.0" @@ -35,7 +35,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Bridge/Twig/CHANGELOG.md b/src/Symfony/Bridge/Twig/CHANGELOG.md index 905d242217c3f..2fbfe125b5aaf 100644 --- a/src/Symfony/Bridge/Twig/CHANGELOG.md +++ b/src/Symfony/Bridge/Twig/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +4.4.0 +----- + + * deprecated to pass `$rootDir` and `$fileLinkFormatter` as 5th and 6th argument respectively to the + `DebugCommand::__construct()` method, swap the variables position. + 4.3.0 ----- diff --git a/src/Symfony/Bridge/Twig/Command/DebugCommand.php b/src/Symfony/Bridge/Twig/Command/DebugCommand.php index 5533a2d98ffa2..e12fd9c04d34f 100644 --- a/src/Symfony/Bridge/Twig/Command/DebugCommand.php +++ b/src/Symfony/Bridge/Twig/Command/DebugCommand.php @@ -42,7 +42,11 @@ class DebugCommand extends Command private $filesystemLoaders; private $fileLinkFormatter; - public function __construct(Environment $twig, string $projectDir = null, array $bundlesMetadata = [], string $twigDefaultPath = null, string $rootDir = null, FileLinkFormatter $fileLinkFormatter = null) + /** + * @param FileLinkFormatter|null $fileLinkFormatter + * @param string|null $rootDir + */ + public function __construct(Environment $twig, string $projectDir = null, array $bundlesMetadata = [], string $twigDefaultPath = null, $fileLinkFormatter = null, $rootDir = null) { parent::__construct(); @@ -50,8 +54,16 @@ public function __construct(Environment $twig, string $projectDir = null, array $this->projectDir = $projectDir; $this->bundlesMetadata = $bundlesMetadata; $this->twigDefaultPath = $twigDefaultPath; - $this->rootDir = $rootDir; - $this->fileLinkFormatter = $fileLinkFormatter; + + if (\is_string($fileLinkFormatter) || $rootDir instanceof FileLinkFormatter) { + @trigger_error(sprintf('Passing a string as "$fileLinkFormatter" 5th argument or an instance of FileLinkFormatter as "$rootDir" 6th argument of the "%s()" method is deprecated since Symfony 4.4, swap the variables position.', __METHOD__), E_USER_DEPRECATED); + + $this->rootDir = $fileLinkFormatter; + $this->fileLinkFormatter = $rootDir; + } else { + $this->fileLinkFormatter = $fileLinkFormatter; + $this->rootDir = $rootDir; + } } protected function configure() diff --git a/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php b/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php index ed76f2e380d2c..f6a52c2e02e91 100644 --- a/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php @@ -342,7 +342,7 @@ private function createCommandTester(array $paths = [], array $bundleMetadata = } $application = new Application(); - $application->add(new DebugCommand($environment, $projectDir, $bundleMetadata, $defaultPath, $rootDir)); + $application->add(new DebugCommand($environment, $projectDir, $bundleMetadata, $defaultPath, null, $rootDir)); $command = $application->find('debug:twig'); return new CommandTester($command); diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index bd6a61f4ee104..c4e6ab29d4ee1 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -22,27 +22,27 @@ }, "require-dev": { "egulias/email-validator": "^2.0", - "symfony/asset": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/finder": "~3.4|~4.0", - "symfony/form": "^4.3", - "symfony/http-foundation": "~4.3", - "symfony/http-kernel": "~3.4|~4.0", - "symfony/mime": "~4.3", + "symfony/asset": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/finder": "^3.4|^4.0|^5.0", + "symfony/form": "^4.3|^5.0", + "symfony/http-foundation": "^4.3|^5.0", + "symfony/http-kernel": "^3.4|^4.0|^5.0", + "symfony/mime": "^4.3|^5.0", "symfony/polyfill-intl-icu": "~1.0", - "symfony/routing": "~3.4|~4.0", - "symfony/templating": "~3.4|~4.0", - "symfony/translation": "^4.2.1", - "symfony/yaml": "~3.4|~4.0", - "symfony/security-acl": "~2.8|~3.0", - "symfony/security-csrf": "~3.4|~4.0", - "symfony/security-http": "~3.4|~4.0", - "symfony/stopwatch": "~3.4|~4.0", - "symfony/console": "~3.4|~4.0", - "symfony/var-dumper": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/web-link": "~3.4|~4.0", - "symfony/workflow": "~4.3" + "symfony/routing": "^3.4|^4.0|^5.0", + "symfony/templating": "^3.4|^4.0|^5.0", + "symfony/translation": "^4.2.1|^5.0", + "symfony/yaml": "^3.4|^4.0|^5.0", + "symfony/security-acl": "^2.8|^3.0", + "symfony/security-csrf": "^3.4|^4.0|^5.0", + "symfony/security-http": "^3.4|^4.0|^5.0", + "symfony/stopwatch": "^3.4|^4.0|^5.0", + "symfony/console": "^3.4|^4.0|^5.0", + "symfony/var-dumper": "^3.4|^4.0|^5.0", + "symfony/expression-language": "^3.4|^4.0|^5.0", + "symfony/web-link": "^3.4|^4.0|^5.0", + "symfony/workflow": "^4.3|^5.0" }, "conflict": { "symfony/console": "<3.4", @@ -77,7 +77,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Bundle/DebugBundle/composer.json b/src/Symfony/Bundle/DebugBundle/composer.json index 76f7a86f96a23..5087c97e08b26 100644 --- a/src/Symfony/Bundle/DebugBundle/composer.json +++ b/src/Symfony/Bundle/DebugBundle/composer.json @@ -18,14 +18,14 @@ "require": { "php": "^7.1.3", "ext-xml": "*", - "symfony/http-kernel": "~3.4|~4.0", - "symfony/twig-bridge": "~3.4|~4.0", - "symfony/var-dumper": "^4.1.1" + "symfony/http-kernel": "^3.4|^4.0|^5.0", + "symfony/twig-bridge": "^3.4|^4.0|^5.0", + "symfony/var-dumper": "^4.1.1|^5.0" }, "require-dev": { - "symfony/config": "~4.2", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/web-profiler-bundle": "~3.4|~4.0" + "symfony/config": "^4.2|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/web-profiler-bundle": "^3.4|^4.0|^5.0" }, "conflict": { "symfony/config": "<4.2", @@ -44,7 +44,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index 8c877f562d38e..52489f7ac2c46 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -1,6 +1,14 @@ CHANGELOG ========= +4.4.0 +----- + + * Deprecated support for `templating` engine in `TemplateController`, use Twig instead + * Deprecated the `$parser` argument of `ControllerResolver::__construct()` and `DelegatingLoader::__construct()` + * Deprecated the `controller_name_converter` and `resolve_controller_name_subscriber` services + * The `ControllerResolver` and `DelegatingLoader` classes have been marked as `final` + 4.3.0 ----- diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/RouterCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/RouterCacheWarmer.php index 7e6f4e5c2ff80..e9eec5e6c7606 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/RouterCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/RouterCacheWarmer.php @@ -12,7 +12,7 @@ namespace Symfony\Bundle\FrameworkBundle\CacheWarmer; use Psr\Container\ContainerInterface; -use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\CompatibilityServiceSubscriberInterface as ServiceSubscriberInterface; use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface; use Symfony\Component\Routing\RouterInterface; diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TranslationsCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TranslationsCacheWarmer.php index c1dde79976814..c8a246bca353d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TranslationsCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TranslationsCacheWarmer.php @@ -12,7 +12,7 @@ namespace Symfony\Bundle\FrameworkBundle\CacheWarmer; use Psr\Container\ContainerInterface; -use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\CompatibilityServiceSubscriberInterface as ServiceSubscriberInterface; use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface; use Symfony\Contracts\Translation\TranslatorInterface; diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ValidatorCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ValidatorCacheWarmer.php index bd1e559a361d0..36e4bb185deb3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ValidatorCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ValidatorCacheWarmer.php @@ -21,6 +21,7 @@ use Symfony\Component\Validator\Mapping\Loader\LoaderInterface; use Symfony\Component\Validator\Mapping\Loader\XmlFileLoader; use Symfony\Component\Validator\Mapping\Loader\YamlFileLoader; +use Symfony\Component\Validator\ValidatorBuilder; use Symfony\Component\Validator\ValidatorBuilderInterface; /** @@ -33,10 +34,14 @@ class ValidatorCacheWarmer extends AbstractPhpFileCacheWarmer private $validatorBuilder; /** - * @param string $phpArrayFile The PHP file where metadata are cached + * @param ValidatorBuilder $validatorBuilder + * @param string $phpArrayFile The PHP file where metadata are cached */ - public function __construct(ValidatorBuilderInterface $validatorBuilder, string $phpArrayFile) + public function __construct($validatorBuilder, string $phpArrayFile) { + if (!$validatorBuilder instanceof ValidatorBuilder && !$validatorBuilder instanceof ValidatorBuilderInterface) { + throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, ValidatorBuilder::class, \is_object($validatorBuilder) ? \get_class($validatorBuilder) : \gettype($validatorBuilder))); + } if (2 < \func_num_args() && func_get_arg(2) instanceof CacheItemPoolInterface) { @trigger_error(sprintf('The CacheItemPoolInterface $fallbackPool argument of "%s()" is deprecated since Symfony 4.2, you should not pass it anymore.', __METHOD__), E_USER_DEPRECATED); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php index 3c5b15fb7e592..7753f1b25f388 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php @@ -65,6 +65,7 @@ protected function execute(InputInterface $input, OutputInterface $output) ['Symfony'], new TableSeparator(), ['Version', Kernel::VERSION], + ['Long-Term Support', 4 === Kernel::MINOR_VERSION ? 'Yes' : 'No'], ['End of maintenance', Kernel::END_OF_MAINTENANCE.(self::isExpired(Kernel::END_OF_MAINTENANCE) ? ' Expired' : '')], ['End of life', Kernel::END_OF_LIFE.(self::isExpired(Kernel::END_OF_LIFE) ? ' Expired' : '')], new TableSeparator(), diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php index b7c4fba0333fe..11c189d4b1b8d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php @@ -163,6 +163,10 @@ protected function execute(InputInterface $input, OutputInterface $output) try { $helper->describe($io, $object, $options); + + if (isset($options['id']) && isset($this->getApplication()->getKernel()->getContainer()->getRemovedIds()[$options['id']])) { + $errorIo->note(sprintf('The "%s" service or alias has been removed or inlined when the container was compiled.', $options['id'])); + } } catch (ServiceNotFoundException $e) { if ('' !== $e->getId() && '@' === $e->getId()[0]) { throw new ServiceNotFoundException($e->getId(), $e->getSourceId(), null, [substr($e->getId(), 1)]); diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php index d02a9824ce2b7..1a1112dbaeb23 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php @@ -27,9 +27,13 @@ class ControllerNameParser { protected $kernel; - public function __construct(KernelInterface $kernel) + public function __construct(KernelInterface $kernel, bool $triggerDeprecation = true) { $this->kernel = $kernel; + + if ($triggerDeprecation) { + @trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.1.', __CLASS__), E_USER_DEPRECATED); + } } /** diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php index 552704f20d42d..e4f5e5dfa54a3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php @@ -18,14 +18,30 @@ /** * @author Fabien Potencier + * + * @final since Symfony 4.4 */ class ControllerResolver extends ContainerControllerResolver { + /** + * @deprecated since Symfony 4.4 + */ protected $parser; - public function __construct(ContainerInterface $container, ControllerNameParser $parser, LoggerInterface $logger = null) + /** + * @param LoggerInterface|null $logger + */ + public function __construct(ContainerInterface $container, $logger = null) { - $this->parser = $parser; + if ($logger instanceof ControllerNameParser) { + @trigger_error(sprintf('Passing a "%s" instance as 2nd argument to "%s()" is deprecated since Symfony 4.4, pass a "%s" instance or null instead.', ControllerNameParser::class, __METHOD__, LoggerInterface::class), E_USER_DEPRECATED); + $this->parser = $logger; + $logger = 2 < \func_num_args() ? func_get_arg(2) : null; + } elseif (2 < \func_num_args() && func_get_arg(2) instanceof ControllerNameParser) { + $this->parser = func_get_arg(2); + } elseif ($logger && !$logger instanceof LoggerInterface) { + throw new \TypeError(sprintf('Argument 2 of "%s()" must be an instance of "%s" or null, "%s" given.', __METHOD__, LoggerInterface::class, \is_object($logger) ? \get_class($logger) : \gettype($logger)), E_USER_DEPRECATED); + } parent::__construct($container, $logger); } @@ -35,7 +51,7 @@ public function __construct(ContainerInterface $container, ControllerNameParser */ protected function createController($controller) { - if (false === strpos($controller, '::') && 2 === substr_count($controller, ':')) { + if ($this->parser && false === strpos($controller, '::') && 2 === substr_count($controller, ':')) { // controller in the a:b:c notation then $deprecatedNotation = $controller; $controller = $this->parser->parse($deprecatedNotation, false); diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php index 2f20678e318e3..9cb7a58f6e856 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php @@ -28,6 +28,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Stamp\StampInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Csrf\CsrfToken; @@ -397,18 +398,19 @@ protected function isCsrfTokenValid(string $id, ?string $token): bool /** * Dispatches a message to the bus. * - * @param object|Envelope $message The message or the message pre-wrapped in an envelope + * @param object|Envelope $message The message or the message pre-wrapped in an envelope + * @param StampInterface[] $stamps * * @final */ - protected function dispatchMessage($message): Envelope + protected function dispatchMessage($message, array $stamps = []): Envelope { if (!$this->container->has('messenger.default_bus')) { $message = class_exists(Envelope::class) ? 'You need to define the "messenger.default_bus" configuration option.' : 'Try running "composer require symfony/messenger".'; throw new \LogicException('The message bus is not enabled in your application. '.$message); } - return $this->container->get('messenger.default_bus')->dispatch($message); + return $this->container->get('messenger.default_bus')->dispatch($message, $stamps); } /** diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/TemplateController.php b/src/Symfony/Bundle/FrameworkBundle/Controller/TemplateController.php index 211c7ce6c8ddc..8e359569f8ced 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/TemplateController.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/TemplateController.php @@ -29,6 +29,10 @@ class TemplateController public function __construct(Environment $twig = null, EngineInterface $templating = null) { + if (null !== $templating) { + @trigger_error(sprintf('Using a "%s" instance for "%s" is deprecated since version 4.4; use a \Twig\Environment instance instead.', EngineInterface::class, __CLASS__), E_USER_DEPRECATED); + } + $this->twig = $twig; $this->templating = $templating; } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/CompatibilityServiceSubscriberInterface.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/CompatibilityServiceSubscriberInterface.php new file mode 100644 index 0000000000000..41d4aa81e99c1 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/CompatibilityServiceSubscriberInterface.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\Bundle\FrameworkBundle\DependencyInjection; + +use Symfony\Component\DependencyInjection\ServiceSubscriberInterface as LegacyServiceSubscriberInterface; +use Symfony\Contracts\Service\ServiceSubscriberInterface; + +if (interface_exists(LegacyServiceSubscriberInterface::class)) { + /** + * @internal + */ + interface CompatibilityServiceSubscriberInterface extends LegacyServiceSubscriberInterface + { + } +} else { + /** + * @internal + */ + interface CompatibilityServiceSubscriberInterface extends ServiceSubscriberInterface + { + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index a1ab515ae2148..db9658088cb11 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -13,6 +13,7 @@ use Doctrine\Common\Annotations\AnnotationRegistry; use Doctrine\Common\Annotations\Reader; +use Http\Client\HttpClient; use Psr\Cache\CacheItemPoolInterface; use Psr\Container\ContainerInterface as PsrContainerInterface; use Psr\Http\Client\ClientInterface; @@ -60,7 +61,6 @@ use Symfony\Component\Form\FormTypeExtensionInterface; use Symfony\Component\Form\FormTypeGuesserInterface; use Symfony\Component\Form\FormTypeInterface; -use Symfony\Component\HttpClient\Psr18Client; use Symfony\Component\HttpClient\ScopingHttpClient; use Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface; use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; @@ -1171,14 +1171,15 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder ->followLinks() ->files() ->filter(function (\SplFileInfo $file) { - return 2 === substr_count($file->getBasename(), '.') && preg_match('/\.\w+$/', $file->getBasename()); + return 2 <= substr_count($file->getBasename(), '.') && preg_match('/\.\w+$/', $file->getBasename()); }) ->in($dirs) ->sortByName() ; foreach ($finder as $file) { - list(, $locale) = explode('.', $file->getBasename(), 3); + $fileNameParts = explode('.', basename($file)); + $locale = $fileNameParts[\count($fileNameParts) - 2]; if (!isset($files[$locale])) { $files[$locale] = []; } @@ -1884,6 +1885,10 @@ private function registerHttpClientConfiguration(array $config, ContainerBuilder $container->removeAlias(ClientInterface::class); } + if (!interface_exists(HttpClient::class)) { + $container->removeDefinition(HttpClient::class); + } + foreach ($config['scoped_clients'] as $name => $scopeConfig) { if ('http_client' === $name) { throw new InvalidArgumentException(sprintf('Invalid scope name: "%s" is reserved.', $name)); @@ -1904,9 +1909,8 @@ private function registerHttpClientConfiguration(array $config, ContainerBuilder $container->registerAliasForArgument($name, HttpClientInterface::class); if ($hasPsr18) { - $container->register('psr18.'.$name, Psr18Client::class) - ->setAutowired(true) - ->setArguments([new Reference($name)]); + $container->setDefinition('psr18.'.$name, new ChildDefinition('psr18.http_client')) + ->replaceArgument(0, new Reference($name)); $container->registerAliasForArgument('psr18.'.$name, ClientInterface::class, $name); } diff --git a/src/Symfony/Bundle/FrameworkBundle/EventListener/ResolveControllerNameSubscriber.php b/src/Symfony/Bundle/FrameworkBundle/EventListener/ResolveControllerNameSubscriber.php index 709df23075a54..169c03277970f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/EventListener/ResolveControllerNameSubscriber.php +++ b/src/Symfony/Bundle/FrameworkBundle/EventListener/ResolveControllerNameSubscriber.php @@ -13,7 +13,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\KernelEvents; /** @@ -21,19 +21,39 @@ * * @author Ryan Weaver * + * @method onKernelRequest(RequestEvent $event) + * * @deprecated since Symfony 4.1 */ class ResolveControllerNameSubscriber implements EventSubscriberInterface { private $parser; - public function __construct(ControllerNameParser $parser) + public function __construct(ControllerNameParser $parser, bool $triggerDeprecation = true) { + if ($triggerDeprecation) { + @trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.1.', __CLASS__), E_USER_DEPRECATED); + } + $this->parser = $parser; } - public function onKernelRequest(GetResponseEvent $event) + /** + * @internal + */ + public function resolveControllerName(...$args) { + $this->onKernelRequest(...$args); + } + + public function __call(string $method, array $args) + { + if ('onKernelRequest' !== $method && 'onKernelRequest' !== strtolower($method)) { + throw new \Error(sprintf('Error: Call to undefined method %s::%s()', \get_class($this), $method)); + } + + $event = $args[0]; + $controller = $event->getRequest()->attributes->get('_controller'); if (\is_string($controller) && false === strpos($controller, '::') && 2 === substr_count($controller, ':')) { // controller in the a:b:c notation then @@ -46,7 +66,7 @@ public function onKernelRequest(GetResponseEvent $event) public static function getSubscribedEvents() { return [ - KernelEvents::REQUEST => ['onKernelRequest', 24], + KernelEvents::REQUEST => ['resolveControllerName', 24], ]; } } diff --git a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php index 97b4ef28a6646..e5c714965c2b0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php +++ b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php @@ -132,7 +132,7 @@ public function build(ContainerBuilder $container) $container->addCompilerPass(new RegisterReverseContainerPass(false), PassConfig::TYPE_AFTER_REMOVING); if ($container->getParameter('kernel.debug')) { - $container->addCompilerPass(new AddDebugLogProcessorPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -32); + $container->addCompilerPass(new AddDebugLogProcessorPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 2); $container->addCompilerPass(new UnusedTagsPass(), PassConfig::TYPE_AFTER_REMOVING); $container->addCompilerPass(new ContainerBuilderDebugDumpPass(), PassConfig::TYPE_BEFORE_REMOVING, -255); $container->addCompilerPass(new CacheCollectorPass(), PassConfig::TYPE_BEFORE_REMOVING); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/http_client.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/http_client.xml index aa29944c472d3..a3f0884365b0a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/http_client.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/http_client.xml @@ -22,5 +22,11 @@ + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml index a060b723b87d1..ac0fcb47a3d40 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml @@ -61,7 +61,7 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml index 8c293ebefc390..54e16f5b4bbbf 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml @@ -46,9 +46,9 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml index 01e93f131ae1a..10e641c83dd17 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml @@ -70,13 +70,13 @@ - + %kernel.debug% %kernel.cache_dir%/%kernel.container_class%Deprecations.log - + @@ -99,7 +99,7 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml index ab5a07e4be8bc..8cc62a72a68e5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml @@ -7,16 +7,21 @@ - + + false + + + + The "%alias_id%" service is deprecated since Symfony 4.3. - + @@ -71,10 +76,15 @@ - - + + + false + + The "%alias_id%" service is deprecated since Symfony 4.3. + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php b/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php index 1e6b90a9bf107..63e6af0be3832 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php +++ b/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php @@ -23,20 +23,33 @@ * to the fully-qualified form (from a:b:c to class::method). * * @author Fabien Potencier + * + * @final since Symfony 4.4 */ class DelegatingLoader extends BaseDelegatingLoader { + /** + * @deprecated since Symfony 4.4 + */ protected $parser; private $loading = false; private $defaultOptions; /** - * @param ControllerNameParser $parser A ControllerNameParser instance - * @param LoaderResolverInterface $resolver A LoaderResolverInterface instance + * @param LoaderResolverInterface $resolver + * @param array $defaultOptions */ - public function __construct(ControllerNameParser $parser, LoaderResolverInterface $resolver, array $defaultOptions = []) + public function __construct($resolver, $defaultOptions = []) { - $this->parser = $parser; + if ($resolver instanceof ControllerNameParser) { + @trigger_error(sprintf('Passing a "%s" instance as first argument to "%s()" is deprecated since Symfony 4.4, pass a "%s" instance instead.', ControllerNameParser::class, __METHOD__, LoaderResolverInterface::class), E_USER_DEPRECATED); + $this->parser = $resolver; + $resolver = $defaultOptions; + $defaultOptions = 2 < \func_num_args() ? func_get_arg(2) : []; + } elseif (2 < \func_num_args() && func_get_arg(2) instanceof ControllerNameParser) { + $this->parser = func_get_arg(2); + } + $this->defaultOptions = $defaultOptions; parent::__construct($resolver); @@ -86,7 +99,7 @@ public function load($resource, $type = null) continue; } - if (2 === substr_count($controller, ':')) { + if ($this->parser && 2 === substr_count($controller, ':')) { $deprecatedNotation = $controller; try { diff --git a/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php b/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php index 3ac249ad50d97..c69de1fc3fbb3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php +++ b/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php @@ -13,12 +13,12 @@ use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\CompatibilityServiceSubscriberInterface as ServiceSubscriberInterface; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\Config\ContainerParametersResource; use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainerInterface; use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; -use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\RouteCollection; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php index 999ee459d66e6..9aedfe37b1fed 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php @@ -180,11 +180,6 @@ function ($path, $catalogue) use ($loadedMessages) { ->willReturnMap($returnValues); } - $kernel - ->expects($this->any()) - ->method('getRootDir') - ->willReturn($this->translationDir); - $kernel ->expects($this->any()) ->method('getBundles') diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php index d73f5765689f7..3eea42c24ec45 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php @@ -63,7 +63,7 @@ public function testGetControllerWithBundleNotation() ->willReturn('Symfony\Bundle\FrameworkBundle\Tests\Controller\ContainerAwareController::testAction') ; - $resolver = $this->createControllerResolver(null, null, $parser); + $resolver = $this->createLegacyControllerResolver(null, null, $parser); $request = Request::create('/'); $request->attributes->set('_controller', $shortName); @@ -105,7 +105,7 @@ class_exists(AbstractControllerTest::class); $container = new Container(); $container->set(TestAbstractController::class, $controller); - $resolver = $this->createControllerResolver(null, $container); + $resolver = $this->createLegacyControllerResolver(null, $container); $request = Request::create('/'); $request->attributes->set('_controller', TestAbstractController::class.'::fooAction'); @@ -127,7 +127,7 @@ class_exists(AbstractControllerTest::class); $container = new Container(); $container->set(DummyController::class, $controller); - $resolver = $this->createControllerResolver(null, $container); + $resolver = $this->createLegacyControllerResolver(null, $container); $request = Request::create('/'); $request->attributes->set('_controller', DummyController::class.'::fooAction'); @@ -176,7 +176,7 @@ class_exists(AbstractControllerTest::class); $this->assertSame($controllerContainer, $controller->getContainer()); } - protected function createControllerResolver(LoggerInterface $logger = null, Psr11ContainerInterface $container = null, ControllerNameParser $parser = null) + protected function createLegacyControllerResolver(LoggerInterface $logger = null, Psr11ContainerInterface $container = null, ControllerNameParser $parser = null) { if (!$parser) { $parser = $this->createMockParser(); @@ -189,6 +189,15 @@ protected function createControllerResolver(LoggerInterface $logger = null, Psr1 return new ControllerResolver($container, $parser, $logger); } + protected function createControllerResolver(LoggerInterface $logger = null, Psr11ContainerInterface $container = null) + { + if (!$container) { + $container = $this->createMockContainer(); + } + + return new ControllerResolver($container, $logger); + } + protected function createMockParser() { return $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser')->disableOriginalConstructor()->getMock(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/TemplateControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/TemplateControllerTest.php index a3abae0298e36..a19d0651bb126 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/TemplateControllerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/TemplateControllerTest.php @@ -31,6 +31,9 @@ public function testTwig() $this->assertEquals('bar', $controller('mytemplate')->getContent()); } + /** + * @group legacy + */ public function testTemplating() { $templating = $this->getMockBuilder(EngineInterface::class)->getMock(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/translations/domain.with.dots.en.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/translations/domain.with.dots.en.yml new file mode 100644 index 0000000000000..5c81ae664d603 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/translations/domain.with.dots.en.yml @@ -0,0 +1,3 @@ +domain: + with: + dots: It works! diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index fab27d4897502..ef1932133eb40 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -819,6 +819,11 @@ public function testTranslator() $files, '->registerTranslatorConfiguration() finds translation resources in default path' ); + $this->assertContains( + strtr(__DIR__.'/Fixtures/translations/domain.with.dots.en.yml', '/', \DIRECTORY_SEPARATOR), + $files, + '->registerTranslatorConfiguration() finds translation resources with dots in domain' + ); $calls = $container->getDefinition('translator.default')->getMethodCalls(); $this->assertEquals(['fr'], $calls[1][1][0]); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/EventListener/ResolveControllerNameSubscriberTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/EventListener/ResolveControllerNameSubscriberTest.php index c8bc4f8a854c5..362f00e95c29b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/EventListener/ResolveControllerNameSubscriberTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/EventListener/ResolveControllerNameSubscriberTest.php @@ -15,14 +15,15 @@ use Symfony\Bundle\FrameworkBundle\EventListener\ResolveControllerNameSubscriber; use Symfony\Bundle\FrameworkBundle\Tests\TestCase; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\HttpKernelInterface; +/** + * @group legacy + */ class ResolveControllerNameSubscriberTest extends TestCase { - /** - * @group legacy - */ public function testReplacesControllerAttribute() { $parser = $this->getMockBuilder(ControllerNameParser::class)->disableOriginalConstructor()->getMock(); @@ -38,6 +39,10 @@ public function testReplacesControllerAttribute() $subscriber = new ResolveControllerNameSubscriber($parser); $subscriber->onKernelRequest(new RequestEvent($httpKernel, $request, HttpKernelInterface::MASTER_REQUEST)); $this->assertEquals('App\\Final\\Format::methodName', $request->attributes->get('_controller')); + + $subscriber = new ChildResolveControllerNameSubscriber($parser); + $subscriber->onKernelRequest(new RequestEvent($httpKernel, $request, HttpKernelInterface::MASTER_REQUEST)); + $this->assertEquals('App\\Final\\Format::methodName', $request->attributes->get('_controller')); } /** @@ -64,3 +69,11 @@ public function provideSkippedControllers() yield [function () {}]; } } + +class ChildResolveControllerNameSubscriber extends ResolveControllerNameSubscriber +{ + public function onKernelRequest(GetResponseEvent $event) + { + parent::onKernelRequest($event); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/translations/domain.with.dots.en.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/translations/domain.with.dots.en.yml new file mode 100644 index 0000000000000..28d7fb750dbb4 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/translations/domain.with.dots.en.yml @@ -0,0 +1 @@ +message: It works! diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php index 229f1419cfa7e..88cb8b28e8ecb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php @@ -63,6 +63,9 @@ public function testPrivateAlias() $tester->run(['command' => 'debug:container']); $this->assertContains('public', $tester->getDisplay()); $this->assertContains('private_alias', $tester->getDisplay()); + + $tester->run(['command' => 'debug:container', 'name' => 'private_alias']); + $this->assertContains('The "private_alias" service or alias has been removed', $tester->getDisplay()); } /** diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/ConcreteMicroKernel.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/ConcreteMicroKernel.php index adbfe942a7a3f..e765c6c23b3c7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/ConcreteMicroKernel.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/ConcreteMicroKernel.php @@ -19,7 +19,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\Kernel; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\Routing\RouteCollectionBuilder; @@ -30,7 +30,7 @@ class ConcreteMicroKernel extends Kernel implements EventSubscriberInterface private $cacheDir; - public function onKernelException(GetResponseForExceptionEvent $event) + public function onKernelException(RequestEvent $event) { if ($event->getException() instanceof Danger) { $event->setResponse(Response::create('It\'s dangerous to go alone. Take this âš”')); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/DelegatingLoaderTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/DelegatingLoaderTest.php index 1d576056ebf8b..daed030f721a2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/DelegatingLoaderTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/DelegatingLoaderTest.php @@ -13,6 +13,10 @@ class DelegatingLoaderTest extends TestCase { + /** + * @group legacy + * @expectedDeprecation Passing a "Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser" instance as first argument to "Symfony\Bundle\FrameworkBundle\Routing\DelegatingLoader::__construct()" is deprecated since Symfony 4.4, pass a "Symfony\Component\Config\Loader\LoaderResolverInterface" instance instead. + */ public function testConstructorApi() { $controllerNameParser = $this->getMockBuilder(ControllerNameParser::class) @@ -24,10 +28,6 @@ public function testConstructorApi() public function testLoadDefaultOptions() { - $controllerNameParser = $this->getMockBuilder(ControllerNameParser::class) - ->disableOriginalConstructor() - ->getMock(); - $loaderResolver = $this->getMockBuilder(LoaderResolverInterface::class) ->disableOriginalConstructor() ->getMock(); @@ -46,7 +46,7 @@ public function testLoadDefaultOptions() ->method('load') ->willReturn($routeCollection); - $delegatingLoader = new DelegatingLoader($controllerNameParser, $loaderResolver, ['utf8' => true]); + $delegatingLoader = new DelegatingLoader($loaderResolver, ['utf8' => true]); $loadedRouteCollection = $delegatingLoader->load('foo'); $this->assertCount(2, $loadedRouteCollection); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php index 43d858e452e5d..61f600a03f265 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php @@ -373,6 +373,21 @@ public function testWarmup() $this->assertEquals('répertoire', $translator->trans('folder')); } + public function testLoadingTranslationFilesWithDotsInMessageDomain() + { + $loader = new \Symfony\Component\Translation\Loader\YamlFileLoader(); + $resourceFiles = [ + 'en' => [ + __DIR__.'/../Fixtures/Resources/translations/domain.with.dots.en.yml', + ], + ]; + + $translator = $this->getTranslator($loader, ['cache_dir' => $this->tmpDir, 'resource_files' => $resourceFiles], 'yml'); + $translator->setLocale('en'); + $translator->setFallbackLocales(['fr']); + $this->assertEquals('It works!', $translator->trans('message', [], 'domain.with.dots')); + } + private function createTranslator($loader, $options, $translatorClass = '\Symfony\Bundle\FrameworkBundle\Translation\Translator', $loaderFomat = 'loader', $defaultLocale = 'en') { if (null === $defaultLocale) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php b/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php index a32e32f898333..1b2dc7e3fa60d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php +++ b/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php @@ -165,7 +165,10 @@ private function addResourceFiles() foreach ($filesByLocale as $locale => $files) { foreach ($files as $key => $file) { // filename is domain.locale.format - list($domain, $locale, $format) = explode('.', basename($file), 3); + $fileNameParts = explode('.', basename($file)); + $format = array_pop($fileNameParts); + $locale = array_pop($fileNameParts); + $domain = implode('.', $fileNameParts); $this->addResource($format, $file, $locale, $domain); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 8182355c16603..3e57b8e6c911f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -18,46 +18,46 @@ "require": { "php": "^7.1.3", "ext-xml": "*", - "symfony/cache": "~4.3", - "symfony/config": "~4.2", - "symfony/dependency-injection": "^4.3", - "symfony/http-foundation": "^4.3", - "symfony/http-kernel": "^4.3", + "symfony/cache": "^4.3|^5.0", + "symfony/config": "^4.2|^5.0", + "symfony/dependency-injection": "^4.4|^5.0", + "symfony/http-foundation": "^4.3|^5.0", + "symfony/http-kernel": "^4.3|^5.0", "symfony/polyfill-mbstring": "~1.0", - "symfony/filesystem": "~3.4|~4.0", - "symfony/finder": "~3.4|~4.0", - "symfony/routing": "^4.3" + "symfony/filesystem": "^3.4|^4.0|^5.0", + "symfony/finder": "^3.4|^4.0|^5.0", + "symfony/routing": "^4.3|^5.0" }, "require-dev": { "doctrine/cache": "~1.0", "fig/link-util": "^1.0", - "symfony/asset": "~3.4|~4.0", - "symfony/browser-kit": "^4.3", - "symfony/console": "^4.3", - "symfony/css-selector": "~3.4|~4.0", - "symfony/dom-crawler": "^4.3", + "symfony/asset": "^3.4|^4.0|^5.0", + "symfony/browser-kit": "^4.3|^5.0", + "symfony/console": "^4.3|^5.0", + "symfony/css-selector": "^3.4|^4.0|^5.0", + "symfony/dom-crawler": "^4.3|^5.0", "symfony/polyfill-intl-icu": "~1.0", - "symfony/form": "^4.3", - "symfony/expression-language": "~3.4|~4.0", - "symfony/http-client": "^4.3", - "symfony/mailer": "^4.3", - "symfony/messenger": "^4.3", - "symfony/mime": "^4.3", - "symfony/process": "~3.4|~4.0", - "symfony/security-csrf": "~3.4|~4.0", - "symfony/security-http": "~3.4|~4.0", - "symfony/serializer": "^4.3", - "symfony/stopwatch": "~3.4|~4.0", - "symfony/translation": "~4.3", - "symfony/templating": "~3.4|~4.0", - "symfony/twig-bundle": "~2.8|~3.2|~4.0", - "symfony/validator": "^4.1", - "symfony/var-dumper": "^4.3", - "symfony/workflow": "^4.3", - "symfony/yaml": "~3.4|~4.0", - "symfony/property-info": "~3.4|~4.0", - "symfony/lock": "~3.4|~4.0", - "symfony/web-link": "~3.4|~4.0", + "symfony/form": "^4.3|^5.0", + "symfony/expression-language": "^3.4|^4.0|^5.0", + "symfony/http-client": "^4.3|^5.0", + "symfony/mailer": "^4.3|^5.0", + "symfony/messenger": "^4.3|^5.0", + "symfony/mime": "^4.3|^5.0", + "symfony/process": "^3.4|^4.0|^5.0", + "symfony/security-csrf": "^3.4|^4.0|^5.0", + "symfony/security-http": "^3.4|^4.0|^5.0", + "symfony/serializer": "^4.3|^5.0", + "symfony/stopwatch": "^3.4|^4.0|^5.0", + "symfony/translation": "^4.3|^5.0", + "symfony/templating": "^3.4|^4.0|^5.0", + "symfony/twig-bundle": "^3.4|^4.0|^5.0", + "symfony/validator": "^4.1|^5.0", + "symfony/var-dumper": "^4.3|^5.0", + "symfony/workflow": "^4.3|^5.0", + "symfony/yaml": "^3.4|^4.0|^5.0", + "symfony/property-info": "^3.4|^4.0|^5.0", + "symfony/lock": "^3.4|^4.0|^5.0", + "symfony/web-link": "^3.4|^4.0|^5.0", "doctrine/annotations": "~1.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0", "twig/twig": "~1.34|~2.4" @@ -100,7 +100,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/UserProvider/LdapFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/UserProvider/LdapFactory.php index f213a32f8b7dc..33e59bfc70e74 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/UserProvider/LdapFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/UserProvider/LdapFactory.php @@ -36,6 +36,7 @@ public function create(ContainerBuilder $container, $id, $config) ->replaceArgument(5, $config['uid_key']) ->replaceArgument(6, $config['filter']) ->replaceArgument(7, $config['password_attribute']) + ->replaceArgument(8, $config['extra_fields']) ; } @@ -52,6 +53,9 @@ public function addConfiguration(NodeDefinition $node) ->scalarNode('base_dn')->isRequired()->cannotBeEmpty()->end() ->scalarNode('search_dn')->end() ->scalarNode('search_password')->end() + ->arrayNode('extra_fields') + ->prototype('scalar')->end() + ->end() ->arrayNode('default_roles') ->beforeNormalization()->ifString()->then(function ($v) { return preg_split('/\s*,\s*/', $v); })->end() ->requiresAtLeastOneElement() diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml index 1d2f0c4e503b3..021acccb2a14b 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml @@ -184,6 +184,7 @@ + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLoginLdap/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLoginLdap/config.yml index d608f309f85d4..622ec0f3ebfb6 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLoginLdap/config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLoginLdap/config.yml @@ -21,6 +21,7 @@ security: search_password: '' default_roles: ROLE_USER uid_key: uid + extra_fields: ['email'] firewalls: main: diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index 2bb411b7d0524..f019a09d954e4 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -18,31 +18,31 @@ "require": { "php": "^7.1.3", "ext-xml": "*", - "symfony/config": "^4.2", - "symfony/dependency-injection": "^4.2", + "symfony/config": "^4.2|^5.0", + "symfony/dependency-injection": "^4.2|^5.0", "symfony/http-kernel": "^4.3", - "symfony/security-core": "~4.3", - "symfony/security-csrf": "~4.2", - "symfony/security-guard": "~4.2", + "symfony/security-core": "^4.3", + "symfony/security-csrf": "^4.2|^5.0", + "symfony/security-guard": "^4.2|^5.0", "symfony/security-http": "^4.3" }, "require-dev": { - "symfony/asset": "~3.4|~4.0", - "symfony/browser-kit": "~4.2", - "symfony/console": "~3.4|~4.0", - "symfony/css-selector": "~3.4|~4.0", - "symfony/dom-crawler": "~3.4|~4.0", - "symfony/form": "~3.4|~4.0", - "symfony/framework-bundle": "~4.2", - "symfony/http-foundation": "~3.4|~4.0", - "symfony/translation": "~3.4|~4.0", - "symfony/twig-bundle": "~4.2", - "symfony/twig-bridge": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0", - "symfony/validator": "~3.4|~4.0", - "symfony/var-dumper": "~3.4|~4.0", - "symfony/yaml": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", + "symfony/asset": "^3.4|^4.0|^5.0", + "symfony/browser-kit": "^4.2|^5.0", + "symfony/console": "^3.4|^4.0|^5.0", + "symfony/css-selector": "^3.4|^4.0|^5.0", + "symfony/dom-crawler": "^3.4|^4.0|^5.0", + "symfony/form": "^3.4|^4.0|^5.0", + "symfony/framework-bundle": "^4.2|^5.0", + "symfony/http-foundation": "^3.4|^4.0|^5.0", + "symfony/translation": "^3.4|^4.0|^5.0", + "symfony/twig-bundle": "^4.2|^5.0", + "symfony/twig-bridge": "^3.4|^4.0|^5.0", + "symfony/process": "^3.4|^4.0|^5.0", + "symfony/validator": "^3.4|^4.0|^5.0", + "symfony/var-dumper": "^3.4|^4.0|^5.0", + "symfony/yaml": "^3.4|^4.0|^5.0", + "symfony/expression-language": "^3.4|^4.0|^5.0", "doctrine/doctrine-bundle": "~1.5", "twig/twig": "~1.34|~2.4" }, @@ -62,7 +62,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Bundle/TwigBundle/CHANGELOG.md b/src/Symfony/Bundle/TwigBundle/CHANGELOG.md index 1be455e4e8288..65b7f4b9130a5 100644 --- a/src/Symfony/Bundle/TwigBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/TwigBundle/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +4.4.0 +----- + + * marked the `TemplateIterator` as `internal` + * added HTML comment to beginning and end of `exception_full.html.twig` + 4.2.0 ----- diff --git a/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheCacheWarmer.php b/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheCacheWarmer.php index f74b1c5325e1f..b6b22b77a4817 100644 --- a/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheCacheWarmer.php +++ b/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheCacheWarmer.php @@ -11,9 +11,11 @@ namespace Symfony\Bundle\TwigBundle\CacheWarmer; +@trigger_error('The '.TemplateCacheCacheWarmer::class.' class is deprecated since version 4.4 and will be removed in 5.0; use Twig instead.', E_USER_DEPRECATED); + use Psr\Container\ContainerInterface; use Symfony\Bundle\FrameworkBundle\CacheWarmer\TemplateFinderInterface; -use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Bundle\TwigBundle\DependencyInjection\CompatibilityServiceSubscriberInterface as ServiceSubscriberInterface; use Symfony\Component\Finder\Finder; use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; use Twig\Environment; @@ -26,6 +28,8 @@ * as the Twig loader will need the cache generated by it. * * @author Fabien Potencier + * + * @deprecated since version 4.4, to be removed in 5.0; use Twig instead. */ class TemplateCacheCacheWarmer implements CacheWarmerInterface, ServiceSubscriberInterface { diff --git a/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheWarmer.php b/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheWarmer.php index cb9af8a4624e1..b31b344f451d2 100644 --- a/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheWarmer.php +++ b/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheWarmer.php @@ -12,7 +12,7 @@ namespace Symfony\Bundle\TwigBundle\CacheWarmer; use Psr\Container\ContainerInterface; -use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Bundle\TwigBundle\DependencyInjection\CompatibilityServiceSubscriberInterface as ServiceSubscriberInterface; use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; use Twig\Environment; use Twig\Error\Error; @@ -28,7 +28,7 @@ class TemplateCacheWarmer implements CacheWarmerInterface, ServiceSubscriberInte private $twig; private $iterator; - public function __construct(ContainerInterface $container, \Traversable $iterator) + public function __construct(ContainerInterface $container, iterable $iterator) { // As this cache warmer is optional, dependencies should be lazy-loaded, that's why a container should be injected. $this->container = $container; diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/CompatibilityServiceSubscriberInterface.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/CompatibilityServiceSubscriberInterface.php new file mode 100644 index 0000000000000..967f732ff59f9 --- /dev/null +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/CompatibilityServiceSubscriberInterface.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\Bundle\TwigBundle\DependencyInjection; + +use Symfony\Component\DependencyInjection\ServiceSubscriberInterface as LegacyServiceSubscriberInterface; +use Symfony\Contracts\Service\ServiceSubscriberInterface; + +if (interface_exists(LegacyServiceSubscriberInterface::class)) { + /** + * @internal + */ + interface CompatibilityServiceSubscriberInterface extends LegacyServiceSubscriberInterface + { + } +} else { + /** + * @internal + */ + interface CompatibilityServiceSubscriberInterface extends ServiceSubscriberInterface + { + } +} diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php index 79a6ad9ae8505..ba7e782378c84 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php @@ -105,6 +105,7 @@ public function process(ContainerBuilder $container) } else { $container->setAlias('twig.loader.filesystem', new Alias('twig.loader.native_filesystem', false)); $container->removeDefinition('templating.engine.twig'); + $container->removeDefinition('twig.cache_warmer'); } if ($container->has('assets.packages')) { diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/console.xml b/src/Symfony/Bundle/TwigBundle/Resources/config/console.xml index 03e75a405f50d..28306e19c5f89 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/console.xml +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/console.xml @@ -12,8 +12,8 @@ %kernel.project_dir% %kernel.bundles_metadata% %twig.default_path% - %kernel.root_dir% + %kernel.root_dir% diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/templating.xml b/src/Symfony/Bundle/TwigBundle/Resources/config/templating.xml index 1eba213f0edf9..6768328f4692b 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/templating.xml +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/templating.xml @@ -17,6 +17,8 @@ + + The "%service_id%" service is deprecated since Symfony 4.4 and will be removed in 5.0. diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml b/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml index 13121a2a189b0..684a68873162b 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml @@ -35,6 +35,8 @@ + + The "%service_id%" service is deprecated since Symfony 4.4 and will be removed in 5.0. diff --git a/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception_full.html.twig b/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception_full.html.twig index e4f220896ecda..4291f1177db3e 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception_full.html.twig +++ b/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception_full.html.twig @@ -137,6 +137,14 @@ {{ exception.message }} ({{ status_code }} {{ status_text }}) {% endblock %} +{% block before_html %} + +{% endblock %} + +{% block after_html %} + +{% endblock %} + {% block body %} {% include '@Twig/Exception/exception.html.twig' %} {% endblock %} diff --git a/src/Symfony/Bundle/TwigBundle/Resources/views/layout.html.twig b/src/Symfony/Bundle/TwigBundle/Resources/views/layout.html.twig index e0f679fccd2a7..faca2e7fbc795 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/views/layout.html.twig +++ b/src/Symfony/Bundle/TwigBundle/Resources/views/layout.html.twig @@ -1,3 +1,4 @@ +{% block before_html %}{% endblock %} @@ -34,3 +35,4 @@ {{ include('@Twig/base_js.html.twig') }} +{% block after_html %}{% endblock %} diff --git a/src/Symfony/Bundle/TwigBundle/TemplateIterator.php b/src/Symfony/Bundle/TwigBundle/TemplateIterator.php index 17aca046f8085..3ad83ef818a32 100644 --- a/src/Symfony/Bundle/TwigBundle/TemplateIterator.php +++ b/src/Symfony/Bundle/TwigBundle/TemplateIterator.php @@ -18,6 +18,8 @@ * Iterator for all templates in bundles and in the application Resources directory. * * @author Fabien Potencier + * + * @internal since Symfony 4.4 */ class TemplateIterator implements \IteratorAggregate { @@ -31,7 +33,7 @@ class TemplateIterator implements \IteratorAggregate * @param KernelInterface $kernel A KernelInterface instance * @param string $rootDir The directory where global templates can be stored * @param array $paths Additional Twig paths to warm - * @param string $defaultPath The directory where global templates can be stored + * @param string|null $defaultPath The directory where global templates can be stored */ public function __construct(KernelInterface $kernel, string $rootDir, array $paths = [], string $defaultPath = null) { @@ -50,40 +52,46 @@ public function getIterator() return $this->templates; } - $this->templates = array_merge( - $this->findTemplatesInDirectory($this->rootDir.'/Resources/views'), - $this->findTemplatesInDirectory($this->defaultPath, null, ['bundles']) - ); + $templates = $this->findTemplatesInDirectory($this->rootDir.'/Resources/views'); + + if (null !== $this->defaultPath) { + $templates = array_merge( + $templates, + $this->findTemplatesInDirectory($this->defaultPath, null, ['bundles']) + ); + } foreach ($this->kernel->getBundles() as $bundle) { $name = $bundle->getName(); if ('Bundle' === substr($name, -6)) { $name = substr($name, 0, -6); } - $this->templates = array_merge( - $this->templates, + $templates = array_merge( + $templates, $this->findTemplatesInDirectory($bundle->getPath().'/Resources/views', $name), - $this->findTemplatesInDirectory($this->rootDir.'/Resources/'.$bundle->getName().'/views', $name), - $this->findTemplatesInDirectory($this->defaultPath.'/bundles/'.$bundle->getName(), $name) + $this->findTemplatesInDirectory($this->rootDir.'/Resources/'.$bundle->getName().'/views', $name) ); + if (null !== $this->defaultPath) { + $templates = array_merge( + $templates, + $this->findTemplatesInDirectory($this->defaultPath.'/bundles/'.$bundle->getName(), $name) + ); + } } foreach ($this->paths as $dir => $namespace) { - $this->templates = array_merge($this->templates, $this->findTemplatesInDirectory($dir, $namespace)); + $templates = array_merge($templates, $this->findTemplatesInDirectory($dir, $namespace)); } - return $this->templates = new \ArrayIterator(array_unique($this->templates)); + return $this->templates = new \ArrayIterator(array_unique($templates)); } /** * Find templates in the given directory. * - * @param string $dir The directory where to look for templates - * @param string|null $namespace The template namespace - * - * @return array + * @return string[] */ - private function findTemplatesInDirectory($dir, $namespace = null, array $excludeDirs = []) + private function findTemplatesInDirectory(string $dir, string $namespace = null, array $excludeDirs = []): array { if (!is_dir($dir)) { return []; diff --git a/src/Symfony/Bundle/TwigBundle/composer.json b/src/Symfony/Bundle/TwigBundle/composer.json index f9a9bf709a743..750dc9f3c2b9b 100644 --- a/src/Symfony/Bundle/TwigBundle/composer.json +++ b/src/Symfony/Bundle/TwigBundle/composer.json @@ -17,26 +17,26 @@ ], "require": { "php": "^7.1.3", - "symfony/config": "~4.2", - "symfony/twig-bridge": "^4.3", - "symfony/http-foundation": "~4.3", - "symfony/http-kernel": "~4.1", + "symfony/config": "^4.2|^5.0", + "symfony/twig-bridge": "^4.4|^5.0", + "symfony/http-foundation": "^4.3|^5.0", + "symfony/http-kernel": "^4.1|^5.0", "symfony/polyfill-ctype": "~1.8", "twig/twig": "~1.41|~2.10" }, "require-dev": { - "symfony/asset": "~3.4|~4.0", - "symfony/stopwatch": "~3.4|~4.0", - "symfony/dependency-injection": "^4.2.5", - "symfony/expression-language": "~3.4|~4.0", - "symfony/finder": "~3.4|~4.0", - "symfony/form": "~3.4|~4.0", - "symfony/routing": "~3.4|~4.0", - "symfony/templating": "~3.4|~4.0", - "symfony/translation": "^4.2", - "symfony/yaml": "~3.4|~4.0", - "symfony/framework-bundle": "~4.3", - "symfony/web-link": "~3.4|~4.0", + "symfony/asset": "^3.4|^4.0|^5.0", + "symfony/stopwatch": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^4.2.5|^5.0", + "symfony/expression-language": "^3.4|^4.0|^5.0", + "symfony/finder": "^3.4|^4.0|^5.0", + "symfony/form": "^3.4|^4.0|^5.0", + "symfony/routing": "^3.4|^4.0|^5.0", + "symfony/templating": "^3.4|^4.0|^5.0", + "symfony/translation": "^4.2|^5.0", + "symfony/yaml": "^3.4|^4.0|^5.0", + "symfony/framework-bundle": "^4.3|^5.0", + "symfony/web-link": "^3.4|^4.0|^5.0", "doctrine/annotations": "~1.0", "doctrine/cache": "~1.0" }, @@ -54,7 +54,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/config.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/config.html.twig index 33fdbad0572b0..bfed460ab7c99 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/config.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/config.html.twig @@ -146,6 +146,9 @@ {{ symfony_status[collector.symfonystate]|upper }} + {% if collector.symfonylts %} +   Long-Term Support + {% endif %} {{ collector.symfonyeom }} {{ collector.symfonyeol }} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base.html.twig index 1bf99d3e0ac7f..580b3b5b0e570 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base.html.twig @@ -14,8 +14,11 @@ {% endblock %} - {% block body '' %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/composer.json b/src/Symfony/Bundle/WebProfilerBundle/composer.json index cbd0f38dd1fc6..93176a593693f 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/composer.json +++ b/src/Symfony/Bundle/WebProfilerBundle/composer.json @@ -17,17 +17,17 @@ ], "require": { "php": "^7.1.3", - "symfony/config": "^4.2", + "symfony/config": "^4.2|^5.0", "symfony/http-kernel": "^4.3", - "symfony/routing": "~3.4|~4.0", - "symfony/twig-bundle": "~4.2", - "symfony/var-dumper": "~3.4|~4.0", + "symfony/routing": "^3.4|^4.0|^5.0", + "symfony/twig-bundle": "^4.2|^5.0", + "symfony/var-dumper": "^3.4|^4.0|^5.0", "twig/twig": "^1.41|^2.10" }, "require-dev": { - "symfony/console": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/stopwatch": "~3.4|~4.0" + "symfony/console": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/stopwatch": "^3.4|^4.0|^5.0" }, "conflict": { "symfony/dependency-injection": "<3.4", @@ -44,7 +44,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Bundle/WebServerBundle/composer.json b/src/Symfony/Bundle/WebServerBundle/composer.json index bf2e67a8368ba..8360c3e04ac75 100644 --- a/src/Symfony/Bundle/WebServerBundle/composer.json +++ b/src/Symfony/Bundle/WebServerBundle/composer.json @@ -17,12 +17,12 @@ ], "require": { "php": "^7.1.3", - "symfony/config": "~3.4|~4.0", - "symfony/console": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/http-kernel": "~3.4|~4.0", + "symfony/config": "^3.4|^4.0|^5.0", + "symfony/console": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/http-kernel": "^3.4|^4.0|^5.0", "symfony/polyfill-ctype": "~1.8", - "symfony/process": "^3.4.2|^4.0.2" + "symfony/process": "^3.4.2|^4.0.2|^5.0" }, "autoload": { "psr-4": { "Symfony\\Bundle\\WebServerBundle\\": "" }, @@ -37,7 +37,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Asset/composer.json b/src/Symfony/Component/Asset/composer.json index cfdd49546f5e2..fc12a5bdb84c3 100644 --- a/src/Symfony/Component/Asset/composer.json +++ b/src/Symfony/Component/Asset/composer.json @@ -22,8 +22,8 @@ "symfony/http-foundation": "" }, "require-dev": { - "symfony/http-foundation": "~3.4|~4.0", - "symfony/http-kernel": "~3.4|~4.0" + "symfony/http-foundation": "^3.4|^4.0|^5.0", + "symfony/http-kernel": "^3.4|^4.0|^5.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Asset\\": "" }, @@ -34,7 +34,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/BrowserKit/composer.json b/src/Symfony/Component/BrowserKit/composer.json index b5efd066a02c0..c51477105776a 100644 --- a/src/Symfony/Component/BrowserKit/composer.json +++ b/src/Symfony/Component/BrowserKit/composer.json @@ -17,13 +17,13 @@ ], "require": { "php": "^7.1.3", - "symfony/dom-crawler": "~3.4|~4.0" + "symfony/dom-crawler": "^3.4|^4.0|^5.0" }, "require-dev": { - "symfony/css-selector": "~3.4|~4.0", - "symfony/http-client": "^4.3", - "symfony/mime": "^4.3", - "symfony/process": "~3.4|~4.0" + "symfony/css-selector": "^3.4|^4.0|^5.0", + "symfony/http-client": "^4.3|^5.0", + "symfony/mime": "^4.3|^5.0", + "symfony/process": "^3.4|^4.0|^5.0" }, "suggest": { "symfony/process": "" @@ -37,7 +37,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Cache/CHANGELOG.md b/src/Symfony/Component/Cache/CHANGELOG.md index 37dd2e11a0e61..1d24a97ed84ed 100644 --- a/src/Symfony/Component/Cache/CHANGELOG.md +++ b/src/Symfony/Component/Cache/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +4.4.0 +----- + + * added support for connecting to Redis Sentinel clusters + 4.3.0 ----- diff --git a/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterSentinelTest.php b/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterSentinelTest.php new file mode 100644 index 0000000000000..96c942654d216 --- /dev/null +++ b/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterSentinelTest.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\AbstractAdapter; +use Symfony\Component\Cache\Adapter\RedisAdapter; + +class RedisAdapterSentinelTest extends AbstractRedisAdapterTest +{ + public static function setupBeforeClass() + { + if (!class_exists('Predis\Client')) { + self::markTestSkipped('The Predis\Client class is required.'); + } + if (!$hosts = getenv('REDIS_SENTINEL_HOSTS')) { + self::markTestSkipped('REDIS_SENTINEL_HOSTS env var is not defined.'); + } + if (!$service = getenv('REDIS_SENTINEL_SERVICE')) { + self::markTestSkipped('REDIS_SENTINEL_SERVICE env var is not defined.'); + } + + self::$redis = AbstractAdapter::createConnection('redis:?host['.str_replace(' ', ']&host[', $hosts).']', ['redis_sentinel' => $service]); + } + + /** + * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid Redis DSN: cannot use both redis_cluster and redis_sentinel at the same time + */ + public function testInvalidDSNHasBothClusterAndSentinel() + { + $dsn = 'redis:?host[redis1]&host[redis2]&host[redis3]&redis_cluster=1&redis_sentinel=mymaster'; + RedisAdapter::createConnection($dsn); + } +} diff --git a/src/Symfony/Component/Cache/Traits/RedisTrait.php b/src/Symfony/Component/Cache/Traits/RedisTrait.php index b2faca651d0d6..6f6d94bbe8986 100644 --- a/src/Symfony/Component/Cache/Traits/RedisTrait.php +++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php @@ -38,6 +38,7 @@ trait RedisTrait 'tcp_keepalive' => 0, 'lazy' => null, 'redis_cluster' => false, + 'redis_sentinel' => null, 'dbindex' => 0, 'failover' => 'none', ]; @@ -146,9 +147,13 @@ public static function createConnection($dsn, array $options = []) throw new InvalidArgumentException(sprintf('Invalid Redis DSN: %s', $dsn)); } + if (isset($params['redis_sentinel']) && !class_exists(\Predis\Client::class)) { + throw new CacheException(sprintf('Redis Sentinel support requires the "predis/predis" package: %s', $dsn)); + } + $params += $query + $options + self::$defaultConnectionOptions; - if (null === $params['class'] && \extension_loaded('redis')) { + if (null === $params['class'] && !isset($params['redis_sentinel']) && \extension_loaded('redis')) { $class = $params['redis_cluster'] ? \RedisCluster::class : (1 < \count($hosts) ? \RedisArray::class : \Redis::class); } else { $class = null === $params['class'] ? \Predis\Client::class : $params['class']; @@ -246,6 +251,12 @@ public static function createConnection($dsn, array $options = []) } elseif (is_a($class, \Predis\Client::class, true)) { if ($params['redis_cluster']) { $params['cluster'] = 'redis'; + if (isset($params['redis_sentinel'])) { + throw new InvalidArgumentException(sprintf('Cannot use both "redis_cluster" and "redis_sentinel" at the same time: %s', $dsn)); + } + } elseif (isset($params['redis_sentinel'])) { + $params['replication'] = 'sentinel'; + $params['service'] = $params['redis_sentinel']; } $params += ['parameters' => []]; $params['parameters'] += [ @@ -268,6 +279,9 @@ public static function createConnection($dsn, array $options = []) } $redis = new $class($hosts, array_diff_key($params, self::$defaultConnectionOptions)); + if (isset($params['redis_sentinel'])) { + $redis->getConnection()->setSentinelTimeout($params['timeout']); + } } elseif (class_exists($class, false)) { throw new InvalidArgumentException(sprintf('"%s" is not a subclass of "Redis", "RedisArray", "RedisCluster" nor "Predis\Client".', $class)); } else { diff --git a/src/Symfony/Component/Cache/composer.json b/src/Symfony/Component/Cache/composer.json index 05bed1cf75485..0e69cbd2f3b22 100644 --- a/src/Symfony/Component/Cache/composer.json +++ b/src/Symfony/Component/Cache/composer.json @@ -26,7 +26,7 @@ "psr/log": "~1.0", "symfony/cache-contracts": "^1.1", "symfony/service-contracts": "^1.1", - "symfony/var-exporter": "^4.2" + "symfony/var-exporter": "^4.2|^5.0" }, "require-dev": { "cache/integration-tests": "dev-master", @@ -34,9 +34,9 @@ "doctrine/dbal": "~2.5", "predis/predis": "~1.1", "psr/simple-cache": "^1.0", - "symfony/config": "~4.2", - "symfony/dependency-injection": "~3.4|~4.1", - "symfony/var-dumper": "^4.1.1" + "symfony/config": "^4.2|^5.0", + "symfony/dependency-injection": "^3.4|^4.1|^5.0", + "symfony/var-dumper": "^4.1.1|^5.0" }, "conflict": { "doctrine/dbal": "<2.5", @@ -52,7 +52,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php index dca5687a2045e..e40d97b2fb89c 100644 --- a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php @@ -523,4 +523,26 @@ public function getChildNodeDefinitions() { return $this->children; } + + /** + * Finds a node defined by the given $nodePath. + * + * @param string $nodePath The path of the node to find. e.g "doctrine.orm.mappings" + */ + public function find(string $nodePath): NodeDefinition + { + $firstPathSegment = (false === $pathSeparatorPos = strpos($nodePath, $this->pathSeparator)) + ? $nodePath + : substr($nodePath, 0, $pathSeparatorPos); + + if (null === $node = ($this->children[$firstPathSegment] ?? null)) { + throw new \RuntimeException(sprintf('Node with name "%s" does not exist in the current node "%s".', $firstPathSegment, $this->name)); + } + + if (false === $pathSeparatorPos) { + return $node; + } + + return $node->find(substr($nodePath, $pathSeparatorPos + \strlen($this->pathSeparator))); + } } diff --git a/src/Symfony/Component/Config/Resource/GlobResource.php b/src/Symfony/Component/Config/Resource/GlobResource.php index fce8f6e2062a0..e33cafc6e0266 100644 --- a/src/Symfony/Component/Config/Resource/GlobResource.php +++ b/src/Symfony/Component/Config/Resource/GlobResource.php @@ -39,7 +39,7 @@ class GlobResource implements \IteratorAggregate, SelfCheckingResourceInterface * * @throws \InvalidArgumentException */ - public function __construct(?string $prefix, string $pattern, bool $recursive, bool $forExclusion = false, array $excludedPrefixes = []) + public function __construct(string $prefix, string $pattern, bool $recursive, bool $forExclusion = false, array $excludedPrefixes = []) { $this->prefix = realpath($prefix) ?: (file_exists($prefix) ? $prefix : false); $this->pattern = $pattern; diff --git a/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php b/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php index 0aa2a08ab40c2..0c414a9b9d263 100644 --- a/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php @@ -13,6 +13,8 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition; +use Symfony\Component\Config\Definition\Builder\NodeDefinition; use Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition; use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; use Symfony\Component\Config\Definition\Processor; @@ -357,6 +359,85 @@ public function testCannotBeEmptyOnConcreteNode() $node->getNode()->finalize([]); } + public function testFindShouldThrowExceptionIfNodeDoesNotExistInRootNode() + { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Node with name "child" does not exist in the current node "root".'); + + $rootNode = new ArrayNodeDefinition('root'); + $rootNode + ->children() + ->arrayNode('social_media_channels')->end() + ->end() + ; + + $rootNode->find('child'); + } + + public function testFindShouldHandleComplexConfigurationProperly() + { + $rootNode = new ArrayNodeDefinition('root'); + $rootNode + ->children() + ->arrayNode('social_media_channels') + ->children() + ->booleanNode('enable')->end() + ->arrayNode('twitter')->end() + ->arrayNode('facebook')->end() + ->arrayNode('instagram') + ->children() + ->booleanNode('enable')->end() + ->arrayNode('accounts')->end() + ->end() + ->end() + ->end() + ->append( + $mailerNode = (new ArrayNodeDefinition('mailer')) + ->children() + ->booleanNode('enable')->end() + ->arrayNode('transports')->end() + ->end() + ) + ->end() + ; + + $this->assertNode('social_media_channels', ArrayNodeDefinition::class, $rootNode->find('social_media_channels')); + $this->assertNode('enable', BooleanNodeDefinition::class, $rootNode->find('social_media_channels.enable')); + $this->assertNode('twitter', ArrayNodeDefinition::class, $rootNode->find('social_media_channels.twitter')); + $this->assertNode('facebook', ArrayNodeDefinition::class, $rootNode->find('social_media_channels.facebook')); + $this->assertNode('instagram', ArrayNodeDefinition::class, $rootNode->find('social_media_channels.instagram')); + $this->assertNode('enable', BooleanNodeDefinition::class, $rootNode->find('social_media_channels.instagram.enable')); + $this->assertNode('accounts', ArrayNodeDefinition::class, $rootNode->find('social_media_channels.instagram.accounts')); + + $this->assertNode('enable', BooleanNodeDefinition::class, $mailerNode->find('enable')); + $this->assertNode('transports', ArrayNodeDefinition::class, $mailerNode->find('transports')); + } + + public function testFindShouldWorkProperlyForNonDefaultPathSeparator() + { + $rootNode = new ArrayNodeDefinition('root'); + $rootNode + ->setPathSeparator('.|') + ->children() + ->arrayNode('mailer.configuration') + ->children() + ->booleanNode('enable')->end() + ->arrayNode('transports')->end() + ->end() + ->end() + ; + + $this->assertNode('mailer.configuration', ArrayNodeDefinition::class, $rootNode->find('mailer.configuration')); + $this->assertNode('enable', BooleanNodeDefinition::class, $rootNode->find('mailer.configuration.|enable')); + $this->assertNode('transports', ArrayNodeDefinition::class, $rootNode->find('mailer.configuration.|transports')); + } + + protected function assertNode(string $expectedName, string $expectedType, NodeDefinition $actualNode): void + { + $this->assertInstanceOf($expectedType, $actualNode); + $this->assertSame($expectedName, $this->getField($actualNode, 'name')); + } + protected function getField($object, $field) { $reflection = new \ReflectionProperty($object, $field); diff --git a/src/Symfony/Component/Config/Tests/Loader/FileLoaderTest.php b/src/Symfony/Component/Config/Tests/Loader/FileLoaderTest.php index b59ace46f937a..0b2e3fb6b9a51 100644 --- a/src/Symfony/Component/Config/Tests/Loader/FileLoaderTest.php +++ b/src/Symfony/Component/Config/Tests/Loader/FileLoaderTest.php @@ -71,6 +71,7 @@ public function testImportWithFileLocatorDelegation() public function testImportWithGlobLikeResource() { $locatorMock = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')->getMock(); + $locatorMock->expects($this->once())->method('locate')->willReturn(''); $loader = new TestFileLoader($locatorMock); $this->assertSame('[foo]', $loader->import('[foo]')); @@ -79,6 +80,7 @@ public function testImportWithGlobLikeResource() public function testImportWithNoGlobMatch() { $locatorMock = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')->getMock(); + $locatorMock->expects($this->once())->method('locate')->willReturn(''); $loader = new TestFileLoader($locatorMock); $this->assertNull($loader->import('./*.abc')); diff --git a/src/Symfony/Component/Config/Tests/Resource/ReflectionClassResourceTest.php b/src/Symfony/Component/Config/Tests/Resource/ReflectionClassResourceTest.php index e22933245d252..cd55345d779e5 100644 --- a/src/Symfony/Component/Config/Tests/Resource/ReflectionClassResourceTest.php +++ b/src/Symfony/Component/Config/Tests/Resource/ReflectionClassResourceTest.php @@ -13,9 +13,9 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Config\Resource\ReflectionClassResource; -use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Messenger\Handler\MessageSubscriberInterface; +use Symfony\Contracts\Service\ServiceSubscriberInterface; class ReflectionClassResourceTest extends TestCase { diff --git a/src/Symfony/Component/Config/composer.json b/src/Symfony/Component/Config/composer.json index c1f2338e5a20c..bbd3a66d19962 100644 --- a/src/Symfony/Component/Config/composer.json +++ b/src/Symfony/Component/Config/composer.json @@ -17,15 +17,15 @@ ], "require": { "php": "^7.1.3", - "symfony/filesystem": "~3.4|~4.0", + "symfony/filesystem": "^3.4|^4.0|^5.0", "symfony/polyfill-ctype": "~1.8" }, "require-dev": { - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~3.4|~4.0", - "symfony/finder": "~3.4|~4.0", - "symfony/messenger": "~4.1", - "symfony/yaml": "~3.4|~4.0" + "symfony/event-dispatcher": "^3.4|^4.0|^5.0", + "symfony/finder": "^3.4|^4.0|^5.0", + "symfony/messenger": "^4.1|^5.0", + "symfony/service-contracts": "^1.1", + "symfony/yaml": "^3.4|^4.0|^5.0" }, "conflict": { "symfony/finder": "<3.4" @@ -42,7 +42,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index da1158528e82d..563eaf33f878f 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -623,6 +623,10 @@ public function find($name) } } + if ($this->has($name)) { + return $this->get($name); + } + $allCommands = $this->commandLoader ? array_merge($this->commandLoader->getNames(), array_keys($this->commands)) : array_keys($this->commands); $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name); $commands = preg_grep('{^'.$expr.'}', $allCommands); @@ -663,8 +667,7 @@ public function find($name) })); } - $exact = \in_array($name, $commands, true) || isset($aliases[$name]); - if (\count($commands) > 1 && !$exact) { + if (\count($commands) > 1) { $usableWidth = $this->terminal->getWidth() - 10; $abbrevs = array_values($commands); $maxLen = 0; @@ -684,7 +687,7 @@ public function find($name) throw new CommandNotFoundException(sprintf("Command \"%s\" is ambiguous.\nDid you mean one of these?\n%s", $name, $suggestions), array_values($commands)); } - return $this->get($exact ? $name : reset($commands)); + return $this->get(reset($commands)); } /** diff --git a/src/Symfony/Component/Console/composer.json b/src/Symfony/Component/Console/composer.json index 5613467fd25a8..a38991dbc610a 100644 --- a/src/Symfony/Component/Console/composer.json +++ b/src/Symfony/Component/Console/composer.json @@ -22,12 +22,12 @@ "symfony/service-contracts": "^1.1" }, "require-dev": { - "symfony/config": "~3.4|~4.0", + "symfony/config": "^3.4|^4.0|^5.0", "symfony/event-dispatcher": "^4.3", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0", - "symfony/var-dumper": "^4.3", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/lock": "^3.4|^4.0|^5.0", + "symfony/process": "^3.4|^4.0|^5.0", + "symfony/var-dumper": "^4.3|^5.0", "psr/log": "~1.0" }, "provide": { @@ -41,7 +41,7 @@ }, "conflict": { "symfony/dependency-injection": "<3.4", - "symfony/event-dispatcher": "<4.3", + "symfony/event-dispatcher": "<4.3|>=5", "symfony/process": "<3.3" }, "autoload": { @@ -53,7 +53,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/CssSelector/composer.json b/src/Symfony/Component/CssSelector/composer.json index 91e64f27d9b2b..65642dc9bc584 100644 --- a/src/Symfony/Component/CssSelector/composer.json +++ b/src/Symfony/Component/CssSelector/composer.json @@ -31,7 +31,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php index a99a000b07fa9..86d19dbd078d8 100644 --- a/src/Symfony/Component/Debug/ErrorHandler.php +++ b/src/Symfony/Component/Debug/ErrorHandler.php @@ -369,18 +369,13 @@ private function reRegister($prev) /** * Handles errors by filtering then logging them according to the configured bit fields. * - * @param int $type One of the E_* constants - * @param string $message - * @param string $file - * @param int $line - * * @return bool Returns false when no handling happens so that the PHP engine can handle the error itself * * @throws \ErrorException When $this->thrownErrors requests so * * @internal */ - public function handleError($type, $message, $file, $line) + public function handleError(int $type, string $message, string $file, int $line): bool { // @deprecated to be removed in Symfony 5.0 if (\PHP_VERSION_ID >= 70300 && $message && '"' === $message[0] && 0 === strpos($message, '"continue') && preg_match('/^"continue(?: \d++)?" targeting switch is equivalent to "break(?: \d++)?"\. Did you mean to use "continue(?: \d++)?"\?$/', $message)) { @@ -443,7 +438,7 @@ public function handleError($type, $message, $file, $line) self::$silencedErrorCache[$id][$message] = $errorAsException; } if (null === $lightTrace) { - return; + return true; } } else { $errorAsException = new \ErrorException($logMessage, 0, $type, $file, $line); diff --git a/src/Symfony/Component/Debug/composer.json b/src/Symfony/Component/Debug/composer.json index 7fd5ff9c93ab8..96fe201bddc8f 100644 --- a/src/Symfony/Component/Debug/composer.json +++ b/src/Symfony/Component/Debug/composer.json @@ -23,7 +23,7 @@ "symfony/http-kernel": "<3.4" }, "require-dev": { - "symfony/http-kernel": "~3.4|~4.0" + "symfony/http-kernel": "^3.4|^4.0|^5.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Debug\\": "" }, @@ -34,7 +34,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index bc54b5f38c36e..7d30dbc646383 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +4.4.0 +----- + + * deprecated support for short factories and short configurators in Yaml + * deprecated `tagged` in favor of `tagged_iterator` + 4.3.0 ----- diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php index 42ee0a25ff959..ca7bb60a9bf62 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php @@ -287,7 +287,7 @@ private function convertParameters(array $parameters, $type, \DOMElement $parent $element->setAttribute('type', 'collection'); $this->convertParameters($value, $type, $element, 'key'); } elseif ($value instanceof TaggedIteratorArgument || ($value instanceof ServiceLocatorArgument && $tag = $value->getTaggedIteratorArgument())) { - $element->setAttribute('type', $value instanceof TaggedIteratorArgument ? 'tagged' : 'tagged_locator'); + $element->setAttribute('type', $value instanceof TaggedIteratorArgument ? 'tagged_iterator' : 'tagged_locator'); $element->setAttribute('tag', $tag->getTag()); if (null !== $tag->getIndexAttribute()) { diff --git a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php index 89dae636de023..cb89bb1758e09 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php @@ -248,7 +248,7 @@ private function dumpValue($value) } } - return new TaggedValue($value instanceof TaggedIteratorArgument ? 'tagged' : 'tagged_locator', $content); + return new TaggedValue($value instanceof TaggedIteratorArgument ? 'tagged_iterator' : 'tagged_locator', $content); } if ($value instanceof IteratorArgument) { diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php index 87b066faf5030..87beeaa392527 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php @@ -115,8 +115,20 @@ function iterator(array $values): IteratorArgument /** * Creates a lazy iterator by tag name. + * + * @deprecated since Symfony 4.4, to be removed in 5.0, use "tagged_iterator" instead. */ function tagged(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null): TaggedIteratorArgument +{ + @trigger_error(__NAMESPACE__.'\tagged() is deprecated since Symfony 4.4 and will be removed in 5.0, use '.__NAMESPACE__.'\tagged_iterator() instead.', E_USER_DEPRECATED); + + return new TaggedIteratorArgument($tag, $indexAttribute, $defaultIndexMethod); +} + +/** + * Creates a lazy iterator by tag name. + */ +function tagged_iterator(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null): TaggedIteratorArgument { return new TaggedIteratorArgument($tag, $indexAttribute, $defaultIndexMethod); } diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index bbe2d5569579b..3980b8618e1d2 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -545,6 +545,9 @@ private function getArgumentsAsPhp(\DOMElement $node, $name, $file, $lowercase = } break; case 'tagged': + @trigger_error(sprintf('Type "%s" of tag <%s> in "%s" is deprecated since Symfony 4.4 and will be removed in 5.0, use "tagged_iterator" instead.', $arg->getAttribute('type'), $name, $file), E_USER_DEPRECATED); + // no break + case 'tagged_iterator': case 'tagged_locator': $type = $arg->getAttribute('type'); $forLocator = 'tagged_locator' === $type; diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index 703620c8a67e3..25cb14b193c40 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -574,28 +574,29 @@ private function parseDefinition($id, $service, $file, array $defaults) /** * Parses a callable. * - * @param string|array $callable A callable - * @param string $parameter A parameter (e.g. 'factory' or 'configurator') - * @param string $id A service identifier - * @param string $file A parsed file + * @param string|array $callable A callable reference + * @param string $parameter The type of callable (e.g. 'factory' or 'configurator') * * @throws InvalidArgumentException When errors occur * * @return string|array|Reference A parsed callable */ - private function parseCallable($callable, $parameter, $id, $file) + private function parseCallable($callable, string $parameter, string $id, string $file) { if (\is_string($callable)) { if ('' !== $callable && '@' === $callable[0]) { if (false === strpos($callable, ':')) { return [$this->resolveServices($callable, $file), '__invoke']; } - throw new InvalidArgumentException(sprintf('The value of the "%s" option for the "%s" service must be the id of the service without the "@" prefix (replace "%s" with "%s").', $parameter, $id, $callable, substr($callable, 1))); + + throw new InvalidArgumentException(sprintf('The value of the "%s" option for the "%s" service must be the id of the service without the "@" prefix (replace "%s" with "%s" in "%s").', $parameter, $id, $callable, substr($callable, 1), $file)); } if (false !== strpos($callable, ':') && false === strpos($callable, '::')) { $parts = explode(':', $callable); + @trigger_error(sprintf('Using short %s syntax for service "%s" is deprecated since Symfony 4.4, use "[\'@%s\', \'%s\']" instead.', $parameter, $id, ...$parts), E_USER_DEPRECATED); + return [$this->resolveServices('@'.$parts[0], $file), $parts[1]]; } @@ -724,7 +725,11 @@ private function resolveServices($value, $file, $isParameter = false) throw new InvalidArgumentException(sprintf('"!service_locator" tag only accepts maps of "@service" references in "%s".', $file)); } } - if (\in_array($value->getTag(), ['tagged', 'tagged_locator'], true)) { + if (\in_array($value->getTag(), ['tagged', 'tagged_iterator', 'tagged_locator'], true)) { + if ('tagged' === $value->getTag()) { + @trigger_error('"!tagged" is deprecated since Symfony 4.4 and will be removed in 5.0, use "!tagged_iterator" instead.', E_USER_DEPRECATED); + } + $forLocator = 'tagged_locator' === $value->getTag(); if (\is_string($argument) && $argument) { diff --git a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd index 05efb9067f82d..2da07fde4e2ee 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd +++ b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd @@ -264,7 +264,9 @@ + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/anonymous.expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/anonymous.expected.yml index b425e53cb9c99..c6a68202757f7 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/anonymous.expected.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/anonymous.expected.yml @@ -7,7 +7,7 @@ services: listener_aggregator: class: Bar\FooClass public: true - arguments: [!tagged listener] + arguments: [!tagged_iterator listener] .2_stdClass~%s: class: stdClass public: false diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/anonymous.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/anonymous.php index 27fc96fc9a407..112b162bcaca9 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/anonymous.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/anonymous.php @@ -15,7 +15,7 @@ ->decorate('decorated', 'decorator42') ->args([ref('decorator42')]); - $s->set('listener_aggregator', FooClass::class)->public()->args([tagged('listener')]); + $s->set('listener_aggregator', FooClass::class)->public()->args([tagged_iterator('listener')]); $s->set(null, stdClass::class)->tag('listener'); }; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services9.php index 4a7172b46c769..04f5858e6b546 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services9.php @@ -128,7 +128,7 @@ $s->set('tagged_iterator', 'Bar') ->public() - ->args([tagged('foo')]); + ->args([tagged_iterator('foo')]); $s->set('runtime_error', 'stdClass') ->args([new Reference('errored_definition', ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE)]) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml index de738521d5bcf..55ec20ee10059 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml @@ -142,7 +142,7 @@ - + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_tagged_arguments.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_tagged_arguments.xml index e3e51d352d837..fcf27a824963f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_tagged_arguments.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_tagged_arguments.xml @@ -6,7 +6,7 @@ - + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/service_instanceof_factory.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/service_instanceof_factory.yml index 4ae8303855ea4..0c90e6bfb3b14 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/service_instanceof_factory.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/service_instanceof_factory.yml @@ -8,7 +8,7 @@ services: public: true Symfony\Component\DependencyInjection\Tests\Fixtures\BarFactory: - arguments: [!tagged 'bar'] + arguments: [!tagged_iterator 'bar'] Symfony\Component\DependencyInjection\Tests\Fixtures\BarInterface: factory: ['@Symfony\Component\DependencyInjection\Tests\Fixtures\BarFactory', 'getDefaultBar'] diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml index 55528921dc1f0..fd2be046f8cd6 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml @@ -157,7 +157,7 @@ services: tagged_iterator: class: Bar arguments: - - !tagged foo + - !tagged_iterator foo public: true Psr\Container\ContainerInterface: alias: service_container diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_with_tagged_argument.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_with_tagged_argument.yml index 5e071d327802e..b30aeb7bff885 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_with_tagged_argument.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_with_tagged_argument.yml @@ -10,7 +10,7 @@ services: - { name: foo } foo_service_tagged_iterator: class: Bar - arguments: [!tagged { tag: foo, index_by: barfoo, default_index_method: foobar }] + arguments: [!tagged_iterator { tag: foo, index_by: barfoo, default_index_method: foobar }] foo_service_tagged_locator: class: Bar arguments: [!tagged_locator { tag: foo, index_by: barfoo, default_index_method: foobar }] diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index 0207abadec711..adb2b925f3d4b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -191,6 +191,9 @@ public function testDeprecatedAliases() $this->assertSame($message, $container->getAlias('alias_for_foobar')->getDeprecationMessage('alias_for_foobar')); } + /** + * @group legacy + */ public function testLoadFactoryShortSyntax() { $container = new ContainerBuilder(); @@ -208,10 +211,13 @@ public function testFactorySyntaxError() $container = new ContainerBuilder(); $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('The value of the "factory" option for the "invalid_factory" service must be the id of the service without the "@" prefix (replace "@factory:method" with "factory:method").'); + $this->expectExceptionMessage('The value of the "factory" option for the "invalid_factory" service must be the id of the service without the "@" prefix (replace "@factory:method" with "factory:method"'); $loader->load('bad_factory_syntax.yml'); } + /** + * @group legacy + */ public function testLoadConfiguratorShortSyntax() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/composer.json b/src/Symfony/Component/DependencyInjection/composer.json index d5182ab7a8a3a..22254796c41da 100644 --- a/src/Symfony/Component/DependencyInjection/composer.json +++ b/src/Symfony/Component/DependencyInjection/composer.json @@ -21,9 +21,9 @@ "symfony/service-contracts": "^1.1.2" }, "require-dev": { - "symfony/yaml": "~3.4|~4.0", - "symfony/config": "^4.3", - "symfony/expression-language": "~3.4|~4.0" + "symfony/yaml": "^3.4|^4.0|^5.0", + "symfony/config": "^4.3|^5.0", + "symfony/expression-language": "^3.4|^4.0|^5.0" }, "suggest": { "symfony/yaml": "", @@ -51,7 +51,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/DomCrawler/AbstractUriElement.php b/src/Symfony/Component/DomCrawler/AbstractUriElement.php index 1ef51df59be6b..ead9dca25bacb 100644 --- a/src/Symfony/Component/DomCrawler/AbstractUriElement.php +++ b/src/Symfony/Component/DomCrawler/AbstractUriElement.php @@ -24,7 +24,7 @@ abstract class AbstractUriElement protected $node; /** - * @var string The method to use for the element + * @var string|null The method to use for the element */ protected $method; @@ -36,7 +36,7 @@ abstract class AbstractUriElement /** * @param \DOMElement $node A \DOMElement instance * @param string $currentUri The URI of the page where the link is embedded (or the base href) - * @param string $method The method to use for the link (GET by default) + * @param string|null $method The method to use for the link (GET by default) * * @throws \InvalidArgumentException if the node is not a link */ @@ -70,7 +70,7 @@ public function getNode() */ public function getMethod() { - return $this->method; + return $this->method ?? 'GET'; } /** diff --git a/src/Symfony/Component/DomCrawler/CHANGELOG.md b/src/Symfony/Component/DomCrawler/CHANGELOG.md index 6c3cd979b6525..1be1f6b41116f 100644 --- a/src/Symfony/Component/DomCrawler/CHANGELOG.md +++ b/src/Symfony/Component/DomCrawler/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +4.4.0 +----- + +* Added `Form::getName()` method. + 4.3.0 ----- diff --git a/src/Symfony/Component/DomCrawler/Form.php b/src/Symfony/Component/DomCrawler/Form.php index 8bdd364347601..13d3bc70a8a87 100644 --- a/src/Symfony/Component/DomCrawler/Form.php +++ b/src/Symfony/Component/DomCrawler/Form.php @@ -250,6 +250,16 @@ public function getMethod() return $this->node->getAttribute('method') ? strtoupper($this->node->getAttribute('method')) : 'GET'; } + /** + * Gets the form name. + * + * If no name is defined on the form, an empty string is returned. + */ + public function getName(): string + { + return $this->node->getAttribute('name'); + } + /** * Returns true if the named field exists. * diff --git a/src/Symfony/Component/DomCrawler/Tests/FormTest.php b/src/Symfony/Component/DomCrawler/Tests/FormTest.php index 2c0ee22c1fc45..f5827f0643e2a 100644 --- a/src/Symfony/Component/DomCrawler/Tests/FormTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/FormTest.php @@ -327,6 +327,18 @@ public function testGetMethodWithOverride() $this->assertEquals('POST', $form->getMethod(), '->getMethod() returns the method attribute value of the form'); } + public function testGetName() + { + $form = $this->createForm('
'); + $this->assertSame('foo', $form->getName()); + } + + public function testGetNameOnFormWithoutName() + { + $form = $this->createForm('
'); + $this->assertSame('', $form->getName()); + } + public function testGetSetValue() { $form = $this->createForm('
'); diff --git a/src/Symfony/Component/DomCrawler/composer.json b/src/Symfony/Component/DomCrawler/composer.json index 31ddb55009e8b..81ddb8cbb8fb6 100644 --- a/src/Symfony/Component/DomCrawler/composer.json +++ b/src/Symfony/Component/DomCrawler/composer.json @@ -21,7 +21,7 @@ "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { - "symfony/css-selector": "~3.4|~4.0", + "symfony/css-selector": "^3.4|^4.0|^5.0", "masterminds/html5": "^2.6" }, "conflict": { @@ -39,7 +39,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Dotenv/composer.json b/src/Symfony/Component/Dotenv/composer.json index 872ff3a4c5be8..772c4b23e5c3b 100644 --- a/src/Symfony/Component/Dotenv/composer.json +++ b/src/Symfony/Component/Dotenv/composer.json @@ -19,7 +19,7 @@ "php": "^7.1.3" }, "require-dev": { - "symfony/process": "~3.4|~4.0" + "symfony/process": "^3.4|^4.0|^5.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Dotenv\\": "" }, @@ -30,7 +30,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/EventDispatcher/composer.json b/src/Symfony/Component/EventDispatcher/composer.json index 8449c4785d93a..fe954318a60ab 100644 --- a/src/Symfony/Component/EventDispatcher/composer.json +++ b/src/Symfony/Component/EventDispatcher/composer.json @@ -20,12 +20,12 @@ "symfony/event-dispatcher-contracts": "^1.1" }, "require-dev": { - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/config": "~3.4|~4.0", - "symfony/http-foundation": "^3.4|^4.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/expression-language": "^3.4|^4.0|^5.0", + "symfony/config": "^3.4|^4.0|^5.0", + "symfony/http-foundation": "^3.4|^4.0|^5.0", "symfony/service-contracts": "^1.1", - "symfony/stopwatch": "~3.4|~4.0", + "symfony/stopwatch": "^3.4|^4.0|^5.0", "psr/log": "~1.0" }, "conflict": { @@ -48,7 +48,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/ExpressionLanguage/composer.json b/src/Symfony/Component/ExpressionLanguage/composer.json index bcab8160430a3..694f1bf4640df 100644 --- a/src/Symfony/Component/ExpressionLanguage/composer.json +++ b/src/Symfony/Component/ExpressionLanguage/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": "^7.1.3", - "symfony/cache": "~3.4|~4.0", + "symfony/cache": "^3.4|^4.0|^5.0", "symfony/service-contracts": "^1.1" }, "autoload": { @@ -29,7 +29,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Filesystem/composer.json b/src/Symfony/Component/Filesystem/composer.json index d13397b42410d..9e0373d2f6662 100644 --- a/src/Symfony/Component/Filesystem/composer.json +++ b/src/Symfony/Component/Filesystem/composer.json @@ -28,7 +28,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Finder/composer.json b/src/Symfony/Component/Finder/composer.json index 05d5d1bb9e9f7..0b1408c0dfd41 100644 --- a/src/Symfony/Component/Finder/composer.json +++ b/src/Symfony/Component/Finder/composer.json @@ -27,7 +27,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index e41c46e907a89..527f84b44a0a5 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +4.4.0 +----- + + * deprecated using `int` or `float` as data for the `NumberType` when the `input` option is set to `string` + 4.3.0 ----- diff --git a/src/Symfony/Component/Form/Extension/Core/Type/NumberType.php b/src/Symfony/Component/Form/Extension/Core/Type/NumberType.php index 54cbb758ee4ec..0ee965d8c458f 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/NumberType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/NumberType.php @@ -40,7 +40,12 @@ public function buildForm(FormBuilderInterface $builder, array $options) $builder->addModelTransformer(new StringToFloatTransformer($options['scale'])); $builder->addModelTransformer(new CallbackTransformer( function ($value) { - return \is_float($value) || \is_int($value) ? (string) $value : $value; + if (\is_float($value) || \is_int($value)) { + @trigger_error(sprintf('Using the %s with float or int data when the "input" option is set to "string" is deprecated since Symfony 4.4 and will throw an exception in 5.0.', self::class), E_USER_DEPRECATED); + $value = (string) $value; + } + + return $value; }, function ($value) { return $value; diff --git a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php index 5acc1adc16b54..4947c994faa5e 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php +++ b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php @@ -140,7 +140,7 @@ public function validate($form, Constraint $formConstraint) // Mark the form with an error if it contains extra fields if (!$config->getOption('allow_extra_fields') && \count($form->getExtraData()) > 0) { $this->context->setConstraint($formConstraint); - $this->context->buildViolation($config->getOption('extra_fields_message')) + $this->context->buildViolation($config->getOption('extra_fields_message', '')) ->setParameter('{{ extra_fields }}', '"'.implode('", "', array_keys($form->getExtraData())).'"') ->setInvalidValue($form->getExtraData()) ->setCode(Form::NO_SUCH_FIELD_ERROR) diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php index aba3b0fae2a5f..e041e40ff0d35 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -146,7 +146,7 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac private $lockSetData = false; /** - * @var string|int|null + * @var string|null */ private $name; @@ -847,6 +847,8 @@ public function add($child, $type = null, array $options = []) throw new UnexpectedTypeException($child, 'string, integer or Symfony\Component\Form\FormInterface'); } + $child = (string) $child; + if (null !== $type && !\is_string($type) && !$type instanceof FormTypeInterface) { throw new UnexpectedTypeException($type, 'string or Symfony\Component\Form\FormTypeInterface'); } diff --git a/src/Symfony/Component/Form/FormConfigBuilder.php b/src/Symfony/Component/Form/FormConfigBuilder.php index fa7bac32bb3c9..09d4dd549c345 100644 --- a/src/Symfony/Component/Form/FormConfigBuilder.php +++ b/src/Symfony/Component/Form/FormConfigBuilder.php @@ -107,7 +107,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface /** * Creates an empty form configuration. * - * @param string|int $name The form name + * @param string|null $name The form name * @param string|null $dataClass The class of the form's data * @param EventDispatcherInterface $dispatcher The event dispatcher * @param array $options The form options @@ -115,7 +115,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface * @throws InvalidArgumentException if the data class is not a valid class or if * the name contains invalid characters */ - public function __construct($name, ?string $dataClass, EventDispatcherInterface $dispatcher, array $options = []) + public function __construct(?string $name, ?string $dataClass, EventDispatcherInterface $dispatcher, array $options = []) { self::validateName($name); @@ -767,15 +767,17 @@ public function getFormConfig() /** * Validates whether the given variable is a valid form name. * - * @param string|int|null $name The tested form name + * @param string|null $name The tested form name * * @throws UnexpectedTypeException if the name is not a string or an integer * @throws InvalidArgumentException if the name contains invalid characters + * + * @internal since Symfony 4.4 */ public static function validateName($name) { - if (null !== $name && !\is_string($name) && !\is_int($name)) { - throw new UnexpectedTypeException($name, 'string, integer or null'); + if (null !== $name && !\is_string($name)) { + throw new UnexpectedTypeException($name, 'string or null'); } if (!self::isValidName($name)) { @@ -793,11 +795,9 @@ public static function validateName($name) * * contains only letters, digits, numbers, underscores ("_"), * hyphens ("-") and colons (":") * - * @param string|null $name The tested form name - * - * @return bool Whether the name is valid + * @final since Symfony 4.4 */ - public static function isValidName($name) + public static function isValidName(?string $name): bool { return '' === $name || null === $name || preg_match('/^[a-zA-Z0-9_][a-zA-Z0-9_\-:]*$/D', $name); } diff --git a/src/Symfony/Component/Form/FormError.php b/src/Symfony/Component/Form/FormError.php index f0898b7665d7f..899631257b1ea 100644 --- a/src/Symfony/Component/Form/FormError.php +++ b/src/Symfony/Component/Form/FormError.php @@ -49,7 +49,12 @@ class FormError */ public function __construct(?string $message, string $messageTemplate = null, array $messageParameters = [], int $messagePluralization = null, $cause = null) { - $this->message = (string) $message; + if (null === $message) { + @trigger_error(sprintf('Passing a null message when instantiating a "%s" is deprecated since Symfony 4.4.', __CLASS__), E_USER_DEPRECATED); + $message = ''; + } + + $this->message = $message; $this->messageTemplate = $messageTemplate ?: $message; $this->messageParameters = $messageParameters; $this->messagePluralization = $messagePluralization; diff --git a/src/Symfony/Component/Form/FormFactory.php b/src/Symfony/Component/Form/FormFactory.php index b397f9a21fbfa..ac38b0a39e647 100644 --- a/src/Symfony/Component/Form/FormFactory.php +++ b/src/Symfony/Component/Form/FormFactory.php @@ -73,7 +73,7 @@ public function createNamedBuilder($name, $type = 'Symfony\Component\Form\Extens $type = $this->registry->getType($type); - $builder = $type->createBuilder($this, $name, $options); + $builder = $type->createBuilder($this, (string) $name, $options); // Explicitly call buildForm() in order to be able to override either // createBuilder() or buildForm() in the resolved form type diff --git a/src/Symfony/Component/Form/ResolvedFormType.php b/src/Symfony/Component/Form/ResolvedFormType.php index 0efde40849f06..d7ab90ff18308 100644 --- a/src/Symfony/Component/Form/ResolvedFormType.php +++ b/src/Symfony/Component/Form/ResolvedFormType.php @@ -13,6 +13,7 @@ use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\OptionsResolver\Exception\ExceptionInterface; use Symfony\Component\OptionsResolver\OptionsResolver; /** @@ -92,7 +93,11 @@ public function getTypeExtensions() */ public function createBuilder(FormFactoryInterface $factory, $name, array $options = []) { - $options = $this->getOptionsResolver()->resolve($options); + try { + $options = $this->getOptionsResolver()->resolve($options); + } catch (ExceptionInterface $e) { + throw new $e(sprintf('An error has occurred resolving the options of the form "%s": %s', \get_class($this->getInnerType()), $e->getMessage()), $e->getCode(), $e); + } // Should be decoupled from the specific option at some point $dataClass = isset($options['data_class']) ? $options['data_class'] : null; diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php index 6d8ab4539d4ee..2ba6272d47a65 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php @@ -77,6 +77,10 @@ public function testDefaultFormattingWithScaleAndStringInput(): void $this->assertSame('12345,68', $form->createView()->vars['value']); } + /** + * @group legacy + * @expectedDeprecation Using the Symfony\Component\Form\Extension\Core\Type\NumberType with float or int data when the "input" option is set to "string" is deprecated since Symfony 4.4 and will throw an exception in 5.0. + */ public function testStringInputWithFloatData(): void { $form = $this->factory->create(static::TESTED_TYPE, 12345.6789, [ @@ -87,6 +91,10 @@ public function testStringInputWithFloatData(): void $this->assertSame('12345,68', $form->createView()->vars['value']); } + /** + * @group legacy + * @expectedDeprecation Using the Symfony\Component\Form\Extension\Core\Type\NumberType with float or int data when the "input" option is set to "string" is deprecated since Symfony 4.4 and will throw an exception in 5.0. + */ public function testStringInputWithIntData(): void { $form = $this->factory->create(static::TESTED_TYPE, 12345, [ diff --git a/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php index bde6c2808dfbd..a46c3de6c769a 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php @@ -46,6 +46,7 @@ protected function setUp() { $this->tokenManager = $this->getMockBuilder(CsrfTokenManagerInterface::class)->getMock(); $this->translator = $this->getMockBuilder(TranslatorInterface::class)->getMock(); + $this->translator->expects($this->any())->method('trans')->willReturnArgument(0); parent::setUp(); } @@ -371,16 +372,11 @@ public function testsTranslateCustomErrorMessage() ->with($csrfToken) ->willReturn(false); - $this->translator->expects($this->once()) - ->method('trans') - ->with('Foobar') - ->willReturn('[trans]Foobar[/trans]'); - $form = $this->factory ->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', null, [ 'csrf_field_name' => 'csrf', 'csrf_token_manager' => $this->tokenManager, - 'csrf_message' => 'Foobar', + 'csrf_message' => '[trans]Foobar[/trans]', 'csrf_token_id' => 'TOKEN_ID', 'compound' => true, ]) diff --git a/src/Symfony/Component/Form/Tests/FormConfigTest.php b/src/Symfony/Component/Form/Tests/FormConfigTest.php index 18dac5528f97f..c241f09973cd1 100644 --- a/src/Symfony/Component/Form/Tests/FormConfigTest.php +++ b/src/Symfony/Component/Form/Tests/FormConfigTest.php @@ -57,11 +57,6 @@ public function getHtml4Ids() [123], // NULL is allowed [null], - // Other types are not - [1.23, 'Symfony\Component\Form\Exception\UnexpectedTypeException'], - [5., 'Symfony\Component\Form\Exception\UnexpectedTypeException'], - [true, 'Symfony\Component\Form\Exception\UnexpectedTypeException'], - [new \stdClass(), 'Symfony\Component\Form\Exception\UnexpectedTypeException'], ]; } diff --git a/src/Symfony/Component/Form/Tests/ResolvedFormTypeTest.php b/src/Symfony/Component/Form/Tests/ResolvedFormTypeTest.php index ba078ad1fd119..e4be02e3df15e 100644 --- a/src/Symfony/Component/Form/Tests/ResolvedFormTypeTest.php +++ b/src/Symfony/Component/Form/Tests/ResolvedFormTypeTest.php @@ -179,6 +179,33 @@ public function testCreateBuilderWithDataClassOption() $this->assertSame('\stdClass', $builder->getDataClass()); } + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException + * @expectedExceptionMessage An error has occurred resolving the options of the form "stdClass": The required option "foo" is missing. + */ + public function testFailsCreateBuilderOnInvalidFormOptionsResolution() + { + $optionsResolver = (new OptionsResolver()) + ->setRequired('foo') + ; + $this->resolvedType = $this->getMockBuilder(ResolvedFormType::class) + ->setConstructorArgs([$this->type, [$this->extension1, $this->extension2], $this->parentResolvedType]) + ->setMethods(['getOptionsResolver', 'getInnerType']) + ->getMock() + ; + $this->resolvedType->expects($this->once()) + ->method('getOptionsResolver') + ->willReturn($optionsResolver) + ; + $this->resolvedType->expects($this->once()) + ->method('getInnerType') + ->willReturn(new \stdClass()) + ; + $factory = $this->getMockFormFactory(); + + $this->resolvedType->createBuilder($factory, 'name'); + } + public function testBuildForm() { $i = 0; diff --git a/src/Symfony/Component/Form/composer.json b/src/Symfony/Component/Form/composer.json index 9d29f76336c9e..e254a2b75414a 100644 --- a/src/Symfony/Component/Form/composer.json +++ b/src/Symfony/Component/Form/composer.json @@ -18,24 +18,24 @@ "require": { "php": "^7.1.3", "symfony/event-dispatcher": "^4.3", - "symfony/intl": "^4.3", - "symfony/options-resolver": "~4.3", + "symfony/intl": "^4.3|^5.0", + "symfony/options-resolver": "~4.3|^5.0", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.0", - "symfony/property-access": "~3.4|~4.0", + "symfony/property-access": "^3.4|^4.0|^5.0", "symfony/service-contracts": "~1.1" }, "require-dev": { "doctrine/collections": "~1.0", - "symfony/validator": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/config": "~3.4|~4.0", - "symfony/console": "^4.3", - "symfony/http-foundation": "~3.4|~4.0", - "symfony/http-kernel": "~4.3", - "symfony/security-csrf": "~3.4|~4.0", - "symfony/translation": "~4.2", - "symfony/var-dumper": "^4.3" + "symfony/validator": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/config": "^3.4|^4.0|^5.0", + "symfony/console": "^4.3|^5.0", + "symfony/http-foundation": "^3.4|^4.0|^5.0", + "symfony/http-kernel": "^4.3|^5.0", + "symfony/security-csrf": "^3.4|^4.0|^5.0", + "symfony/translation": "^4.2|^5.0", + "symfony/var-dumper": "^4.3|^5.0" }, "conflict": { "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", @@ -62,7 +62,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/HttpClient/CHANGELOG.md b/src/Symfony/Component/HttpClient/CHANGELOG.md index 44594a71d0827..b2f900bf39b96 100644 --- a/src/Symfony/Component/HttpClient/CHANGELOG.md +++ b/src/Symfony/Component/HttpClient/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +4.4.0 +----- + + * made `Psr18Client` implement relevant PSR-17 factories + * added `HttplugClient` + 4.3.0 ----- diff --git a/src/Symfony/Component/HttpClient/HttplugClient.php b/src/Symfony/Component/HttpClient/HttplugClient.php new file mode 100644 index 0000000000000..6c612ce13ceb2 --- /dev/null +++ b/src/Symfony/Component/HttpClient/HttplugClient.php @@ -0,0 +1,120 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpClient; + +use Http\Client\Exception\NetworkException; +use Http\Client\Exception\RequestException; +use Http\Client\HttpClient; +use Http\Message\RequestFactory; +use Http\Message\StreamFactory; +use Http\Message\UriFactory; +use Psr\Http\Client\ClientInterface; +use Psr\Http\Client\NetworkExceptionInterface; +use Psr\Http\Client\RequestExceptionInterface; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseFactoryInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\StreamFactoryInterface; +use Psr\Http\Message\StreamInterface; +use Psr\Http\Message\UriInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; + +if (!interface_exists(HttpClient::class)) { + throw new \LogicException('You cannot use "Symfony\Component\HttpClient\HttplugClient" as the "php-http/httplug" package is not installed. Try running "composer require php-http/httplug".'); +} + +if (!interface_exists(ClientInterface::class)) { + throw new \LogicException('You cannot use "Symfony\Component\HttpClient\HttplugClient" as the "psr/http-client" package is not installed. Try running "composer require psr/http-client".'); +} + +if (!interface_exists(RequestFactory::class)) { + throw new \LogicException('You cannot use "Symfony\Component\HttpClient\HttplugClient" as the "php-http/message-factory" package is not installed. Try running "composer require nyholm/psr7".'); +} + +/** + * An adapter to turn a Symfony HttpClientInterface into an Httplug client. + * + * Run "composer require psr/http-client" to install the base ClientInterface. Run + * "composer require nyholm/psr7" to install an efficient implementation of response + * and stream factories with flex-provided autowiring aliases. + * + * @author Nicolas Grekas + */ +final class HttplugClient implements HttpClient, RequestFactory, StreamFactory, UriFactory +{ + private $client; + + public function __construct(HttpClientInterface $client = null, ResponseFactoryInterface $responseFactory = null, StreamFactoryInterface $streamFactory = null) + { + $this->client = new Psr18Client($client, $responseFactory, $streamFactory); + } + + /** + * {@inheritdoc} + */ + public function sendRequest(RequestInterface $request): ResponseInterface + { + try { + return $this->client->sendRequest($request); + } catch (RequestExceptionInterface $e) { + throw new RequestException($e->getMessage(), $request, $e); + } catch (NetworkExceptionInterface $e) { + throw new NetworkException($e->getMessage(), $request, $e); + } + } + + /** + * {@inheritdoc} + */ + public function createRequest($method, $uri, array $headers = [], $body = null, $protocolVersion = '1.1'): RequestInterface + { + $request = $this->client + ->createRequest($method, $uri) + ->withProtocolVersion($protocolVersion) + ->withBody($this->createStream($body)) + ; + + foreach ($headers as $name => $value) { + $request = $request->withAddedHeader($name, $value); + } + + return $request; + } + + /** + * {@inheritdoc} + */ + public function createStream($body = null): StreamInterface + { + if ($body instanceof StreamInterface) { + return $body; + } + + if (\is_string($body ?? '')) { + return $this->client->createStream($body ?? ''); + } + + if (\is_resource($body)) { + return $this->client->createStreamFromResource($body); + } + + throw new \InvalidArgumentException(sprintf('%s() expects string, resource or StreamInterface, %s given.', __METHOD__, \gettype($body))); + } + + /** + * {@inheritdoc} + */ + public function createUri($uri = ''): UriInterface + { + return $uri instanceof UriInterface ? $uri : $this->client->createUri($uri); + } +} diff --git a/src/Symfony/Component/HttpClient/Psr18Client.php b/src/Symfony/Component/HttpClient/Psr18Client.php index a438b7ce94ee4..de199962ff35f 100644 --- a/src/Symfony/Component/HttpClient/Psr18Client.php +++ b/src/Symfony/Component/HttpClient/Psr18Client.php @@ -12,16 +12,26 @@ namespace Symfony\Component\HttpClient; use Nyholm\Psr7\Factory\Psr17Factory; +use Nyholm\Psr7\Request; +use Nyholm\Psr7\Uri; use Psr\Http\Client\ClientInterface; use Psr\Http\Client\NetworkExceptionInterface; use Psr\Http\Client\RequestExceptionInterface; +use Psr\Http\Message\RequestFactoryInterface; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\StreamFactoryInterface; +use Psr\Http\Message\StreamInterface; +use Psr\Http\Message\UriFactoryInterface; +use Psr\Http\Message\UriInterface; use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; +if (!interface_exists(RequestFactoryInterface::class)) { + throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\Psr18Client" as the "psr/http-factory" package is not installed. Try running "composer require nyholm/psr7".'); +} + if (!interface_exists(ClientInterface::class)) { throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\Psr18Client" as the "psr/http-client" package is not installed. Try running "composer require psr/http-client".'); } @@ -37,7 +47,7 @@ * * @experimental in 4.3 */ -final class Psr18Client implements ClientInterface +final class Psr18Client implements ClientInterface, RequestFactoryInterface, StreamFactoryInterface, UriFactoryInterface { private $client; private $responseFactory; @@ -62,6 +72,9 @@ public function __construct(HttpClientInterface $client = null, ResponseFactoryI $this->streamFactory = $this->streamFactory ?? $psr17Factory; } + /** + * {@inheritdoc} + */ public function sendRequest(RequestInterface $request): ResponseInterface { try { @@ -88,6 +101,62 @@ public function sendRequest(RequestInterface $request): ResponseInterface throw new Psr18NetworkException($e, $request); } } + + /** + * {@inheritdoc} + */ + public function createRequest(string $method, $uri): RequestInterface + { + if ($this->responseFactory instanceof RequestFactoryInterface) { + return $this->responseFactory->createRequest($method, $uri); + } + + if (!class_exists(Request::class)) { + throw new \LogicException(sprintf('You cannot use "%s()" as the "nyholm/psr7" package is not installed. Try running "composer require nyholm/psr7".', __METHOD__)); + } + + return new Request($method, $uri); + } + + /** + * {@inheritdoc} + */ + public function createStream(string $content = ''): StreamInterface + { + return $this->streamFactory->createStream($content); + } + + /** + * {@inheritdoc} + */ + public function createStreamFromFile(string $filename, string $mode = 'r'): StreamInterface + { + return $this->streamFactory->createStreamFromFile($filename, $mode); + } + + /** + * {@inheritdoc} + */ + public function createStreamFromResource($resource): StreamInterface + { + return $this->streamFactory->createStreamFromResource($resource); + } + + /** + * {@inheritdoc} + */ + public function createUri(string $uri = ''): UriInterface + { + if ($this->responseFactory instanceof UriFactoryInterface) { + return $this->responseFactory->createUri($uri); + } + + if (!class_exists(Uri::class)) { + throw new \LogicException(sprintf('You cannot use "%s()" as the "nyholm/psr7" package is not installed. Try running "composer require nyholm/psr7".', __METHOD__)); + } + + return new Uri($uri); + } } /** diff --git a/src/Symfony/Component/HttpClient/Tests/HttplugClientTest.php b/src/Symfony/Component/HttpClient/Tests/HttplugClientTest.php new file mode 100644 index 0000000000000..4f3e92d3e1ccf --- /dev/null +++ b/src/Symfony/Component/HttpClient/Tests/HttplugClientTest.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpClient\Tests; + +use Http\Client\Exception\NetworkException; +use Http\Client\Exception\RequestException; +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpClient\HttplugClient; +use Symfony\Component\HttpClient\NativeHttpClient; +use Symfony\Contracts\HttpClient\Test\TestHttpServer; + +class HttplugClientTest extends TestCase +{ + private static $server; + + public static function setUpBeforeClass() + { + TestHttpServer::start(); + } + + public function testSendRequest() + { + $client = new HttplugClient(new NativeHttpClient()); + + $response = $client->sendRequest($client->createRequest('GET', 'http://localhost:8057')); + + $this->assertSame(200, $response->getStatusCode()); + $this->assertSame('application/json', $response->getHeaderLine('content-type')); + + $body = json_decode((string) $response->getBody(), true); + + $this->assertSame('HTTP/1.1', $body['SERVER_PROTOCOL']); + } + + public function testPostRequest() + { + $client = new HttplugClient(new NativeHttpClient()); + + $request = $client->createRequest('POST', 'http://localhost:8057/post') + ->withBody($client->createStream('foo=0123456789')); + + $response = $client->sendRequest($request); + $body = json_decode((string) $response->getBody(), true); + + $this->assertSame(['foo' => '0123456789', 'REQUEST_METHOD' => 'POST'], $body); + } + + public function testNetworkException() + { + $client = new HttplugClient(new NativeHttpClient()); + + $this->expectException(NetworkException::class); + $client->sendRequest($client->createRequest('GET', 'http://localhost:8058')); + } + + public function testRequestException() + { + $client = new HttplugClient(new NativeHttpClient()); + + $this->expectException(RequestException::class); + $client->sendRequest($client->createRequest('BAD.METHOD', 'http://localhost:8057')); + } +} diff --git a/src/Symfony/Component/HttpClient/composer.json b/src/Symfony/Component/HttpClient/composer.json index 3289ef3a6d748..4351f97ff96ac 100644 --- a/src/Symfony/Component/HttpClient/composer.json +++ b/src/Symfony/Component/HttpClient/composer.json @@ -15,6 +15,7 @@ } ], "provide": { + "php-http/client-implementation": "*", "psr/http-client-implementation": "1.0", "symfony/http-client-implementation": "1.1" }, @@ -26,9 +27,10 @@ }, "require-dev": { "nyholm/psr7": "^1.0", + "php-http/httplug": "^1.0|^2.0", "psr/http-client": "^1.0", - "symfony/http-kernel": "^4.3", - "symfony/process": "^4.2" + "symfony/http-kernel": "^4.3|^5.0", + "symfony/process": "^4.2|^5.0" }, "autoload": { "psr-4": { "Symfony\\Component\\HttpClient\\": "" }, @@ -39,7 +41,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php index e217820950057..443c0288891fb 100644 --- a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php +++ b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php @@ -204,7 +204,7 @@ public function prepare(Request $request) if (!$this->headers->has('Accept-Ranges')) { // Only accept ranges on safe HTTP methods - $this->headers->set('Accept-Ranges', $request->isMethodSafe(false) ? 'bytes' : 'none'); + $this->headers->set('Accept-Ranges', $request->isMethodSafe() ? 'bytes' : 'none'); } if (self::$trustXSendfileTypeHeader && $request->headers->has('X-Sendfile-Type')) { diff --git a/src/Symfony/Component/HttpFoundation/CHANGELOG.md b/src/Symfony/Component/HttpFoundation/CHANGELOG.md index 54acd6ae10bde..7ecbdffa9e2bd 100644 --- a/src/Symfony/Component/HttpFoundation/CHANGELOG.md +++ b/src/Symfony/Component/HttpFoundation/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +4.4.0 +----- + + * passing arguments to `Request::isMethodSafe()` is deprecated. + 4.3.0 ----- diff --git a/src/Symfony/Component/HttpFoundation/RedirectResponse.php b/src/Symfony/Component/HttpFoundation/RedirectResponse.php index 8d04aa42c9d87..1d4f37cae3324 100644 --- a/src/Symfony/Component/HttpFoundation/RedirectResponse.php +++ b/src/Symfony/Component/HttpFoundation/RedirectResponse.php @@ -34,6 +34,11 @@ class RedirectResponse extends Response */ public function __construct(?string $url, int $status = 302, array $headers = []) { + if (null === $url) { + @trigger_error(sprintf('Passing a null url when instantiating a "%s" is deprecated since Symfony 4.4.', __CLASS__), E_USER_DEPRECATED); + $url = ''; + } + parent::__construct('', $status, $headers); $this->setTargetUrl($url); @@ -82,7 +87,7 @@ public function getTargetUrl() */ public function setTargetUrl($url) { - if (empty($url)) { + if ('' === ($url ?? '')) { throw new \InvalidArgumentException('Cannot redirect to an empty URL.'); } diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index fffe2ab81ec0e..83cdf60169a75 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -1437,15 +1437,12 @@ public function isMethod($method) * * @see https://tools.ietf.org/html/rfc7231#section-4.2.1 * - * @param bool $andCacheable Adds the additional condition that the method should be cacheable. True by default. - * * @return bool */ - public function isMethodSafe(/* $andCacheable = true */) + public function isMethodSafe() { - if (!\func_num_args() || func_get_arg(0)) { - // setting $andCacheable to false should be deprecated in 4.1 - throw new \BadMethodCallException('Checking only for cacheable HTTP methods with Symfony\Component\HttpFoundation\Request::isMethodSafe() is not supported.'); + if (\func_num_args() > 0) { + @trigger_error(sprintf('Passing arguments to "%s()" has been deprecated since Symfony 4.4; use "%s::isMethodCacheable() to check if the method is cacheable instead."', __METHOD__, __CLASS__), E_USER_DEPRECATED); } return \in_array($this->getMethod(), ['GET', 'HEAD', 'OPTIONS', 'TRACE']); diff --git a/src/Symfony/Component/HttpFoundation/Tests/RedirectResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/RedirectResponseTest.php index 5f6a8ac0883f4..24d703d52bfb7 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RedirectResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RedirectResponseTest.php @@ -28,10 +28,11 @@ public function testGenerateMetaRedirect() /** * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Cannot redirect to an empty URL. */ - public function testRedirectResponseConstructorNullUrl() + public function testRedirectResponseConstructorEmptyUrl() { - $response = new RedirectResponse(null); + $response = new RedirectResponse(''); } /** diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index ab0dcf6818168..c1168f5e45d4e 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -2115,7 +2115,7 @@ public function testMethodSafe($method, $safe) { $request = new Request(); $request->setMethod($method); - $this->assertEquals($safe, $request->isMethodSafe(false)); + $this->assertEquals($safe, $request->isMethodSafe()); } public function methodSafeProvider() @@ -2134,16 +2134,6 @@ public function methodSafeProvider() ]; } - /** - * @expectedException \BadMethodCallException - */ - public function testMethodSafeChecksCacheable() - { - $request = new Request(); - $request->setMethod('OPTIONS'); - $request->isMethodSafe(); - } - /** * @dataProvider methodCacheableProvider */ diff --git a/src/Symfony/Component/HttpFoundation/composer.json b/src/Symfony/Component/HttpFoundation/composer.json index f30975114f604..efc4b94255588 100644 --- a/src/Symfony/Component/HttpFoundation/composer.json +++ b/src/Symfony/Component/HttpFoundation/composer.json @@ -17,12 +17,12 @@ ], "require": { "php": "^7.1.3", - "symfony/mime": "^4.3", + "symfony/mime": "^4.3|^5.0", "symfony/polyfill-mbstring": "~1.1" }, "require-dev": { "predis/predis": "~1.0", - "symfony/expression-language": "~3.4|~4.0" + "symfony/expression-language": "^3.4|^4.0|^5.0" }, "autoload": { "psr-4": { "Symfony\\Component\\HttpFoundation\\": "" }, @@ -33,7 +33,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/HttpKernel/CHANGELOG.md b/src/Symfony/Component/HttpKernel/CHANGELOG.md index b1a5f5101b41d..841b8c66351da 100644 --- a/src/Symfony/Component/HttpKernel/CHANGELOG.md +++ b/src/Symfony/Component/HttpKernel/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +4.4.0 +----- + +* The `DebugHandlersListener` class has been marked as `final` + 4.3.0 ----- diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver.php index 86ceab2f264c1..0a5f618de07ff 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver.php @@ -55,14 +55,16 @@ public function getArguments(Request $request, $controller) $resolved = $resolver->resolve($request, $metadata); - if (!$resolved instanceof \Generator) { - throw new \InvalidArgumentException(sprintf('%s::resolve() must yield at least one value.', \get_class($resolver))); - } - + $atLeastOne = false; foreach ($resolved as $append) { + $atLeastOne = true; $arguments[] = $append; } + if (!$atLeastOne) { + throw new \InvalidArgumentException(sprintf('%s::resolve() must yield at least one value.', \get_class($resolver))); + } + // continue to the next controller argument continue 2; } diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/VariadicValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/VariadicValueResolver.php index 7ee2d7af5cee2..a388bd823d448 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/VariadicValueResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/VariadicValueResolver.php @@ -41,8 +41,6 @@ public function resolve(Request $request, ArgumentMetadata $argument) throw new \InvalidArgumentException(sprintf('The action argument "...$%1$s" is required to be an array, the request attribute "%1$s" contains a type of "%2$s" instead.', $argument->getName(), \gettype($values))); } - foreach ($values as $value) { - yield $value; - } + yield from $values; } } diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentValueResolverInterface.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentValueResolverInterface.php index fd7b09ecf2ede..21d874364a754 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentValueResolverInterface.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentValueResolverInterface.php @@ -37,7 +37,7 @@ public function supports(Request $request, ArgumentMetadata $argument); * @param Request $request * @param ArgumentMetadata $argument * - * @return \Generator + * @return iterable */ public function resolve(Request $request, ArgumentMetadata $argument); } diff --git a/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php b/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php index 8ee9b0b9388dc..527a6a8a36466 100644 --- a/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php +++ b/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php @@ -42,30 +42,24 @@ public function createArgumentMetadata($controller) /** * Returns an associated type to the given parameter if available. - * - * @param \ReflectionParameter $parameter - * - * @return string|null */ - private function getType(\ReflectionParameter $parameter, \ReflectionFunctionAbstract $function) + private function getType(\ReflectionParameter $parameter, \ReflectionFunctionAbstract $function): ?string { if (!$type = $parameter->getType()) { - return; + return null; } $name = $type->getName(); - $lcName = strtolower($name); - if ('self' !== $lcName && 'parent' !== $lcName) { - return $name; - } - if (!$function instanceof \ReflectionMethod) { - return; - } - if ('self' === $lcName) { - return $function->getDeclaringClass()->name; - } - if ($parent = $function->getDeclaringClass()->getParentClass()) { - return $parent->name; + if ($function instanceof \ReflectionMethod) { + $lcName = strtolower($name); + switch ($lcName) { + case 'self': + return $function->getDeclaringClass()->name; + case 'parent': + return ($parent = $function->getDeclaringClass()->getParentClass()) ? $parent->name : null; + } } + + return $name; } } diff --git a/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php index c3c3f94eadefa..ddc331af62175 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php @@ -83,6 +83,7 @@ public function collect(Request $request, Response $response, \Exception $except $this->data['symfony_state'] = $this->determineSymfonyState(); $this->data['symfony_minor_version'] = sprintf('%s.%s', Kernel::MAJOR_VERSION, Kernel::MINOR_VERSION); + $this->data['symfony_lts'] = 4 === Kernel::MINOR_VERSION; $eom = \DateTime::createFromFormat('m/Y', Kernel::END_OF_MAINTENANCE); $eol = \DateTime::createFromFormat('m/Y', Kernel::END_OF_LIFE); $this->data['symfony_eom'] = $eom->format('F Y'); @@ -169,6 +170,14 @@ public function getSymfonyMinorVersion() return $this->data['symfony_minor_version']; } + /** + * Returns if the current Symfony version is a Long-Term Support one. + */ + public function isSymfonyLts(): bool + { + return $this->data['symfony_lts']; + } + /** * Returns the human redable date when this Symfony version ends its * maintenance period. diff --git a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php index b28ad7b83e5e4..493cfb1f4ffca 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php @@ -30,6 +30,8 @@ * Configures errors and exceptions handlers. * * @author Nicolas Grekas + * + * @final since Symfony 4.4 */ class DebugHandlersListener implements EventSubscriberInterface { diff --git a/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php b/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php index fc4ba56d9b0d8..f7d3dc69566ab 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php @@ -79,7 +79,7 @@ public function onKernelRequest(GetResponseEvent $event) protected function validateRequest(Request $request) { // is the Request safe? - if (!$request->isMethodSafe(false)) { + if (!$request->isMethodSafe()) { throw new AccessDeniedHttpException(); } diff --git a/src/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.php b/src/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.php index 9a700a9b1158b..08f7c167d5e90 100644 --- a/src/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.php +++ b/src/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.php @@ -52,6 +52,8 @@ public function __construct($templating = null, UriSigner $signer = null, string * @param EngineInterface|Environment|null $templating An EngineInterface or an Environment instance * * @throws \InvalidArgumentException + * + * @internal */ public function setTemplating($templating) { diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php index 051285aeba60e..982e8cdb9ae30 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php @@ -207,7 +207,7 @@ public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQ $this->traces[$this->getTraceKey($request)] = []; - if (!$request->isMethodSafe(false)) { + if (!$request->isMethodSafe()) { $response = $this->invalidate($request, $catch); } elseif ($request->headers->has('expect') || !$request->isMethodCacheable()) { $response = $this->pass($request, $catch); diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index b9edce2354d63..e6532861215ab 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -73,15 +73,15 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private $requestStackSize = 0; private $resetServices = false; - const VERSION = '4.3.2-DEV'; - const VERSION_ID = 40302; + const VERSION = '4.4.0-DEV'; + const VERSION_ID = 40400; const MAJOR_VERSION = 4; - const MINOR_VERSION = 3; - const RELEASE_VERSION = 2; + const MINOR_VERSION = 4; + const RELEASE_VERSION = 0; const EXTRA_VERSION = 'DEV'; - const END_OF_MAINTENANCE = '01/2020'; - const END_OF_LIFE = '07/2020'; + const END_OF_MAINTENANCE = '11/2022'; + const END_OF_LIFE = '11/2023'; public function __construct(string $environment, bool $debug) { diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolverTest.php index 45aa9ef26aad5..017ffa0a4165c 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolverTest.php @@ -185,7 +185,7 @@ public function testGetArgumentWithoutArray() $resolver = new ArgumentResolver($factory, [$valueResolver]); $valueResolver->expects($this->any())->method('supports')->willReturn(true); - $valueResolver->expects($this->any())->method('resolve')->willReturn('foo'); + $valueResolver->expects($this->any())->method('resolve')->willReturn([]); $request = Request::create('/'); $request->attributes->set('foo', 'foo'); diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/ConfigDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/ConfigDataCollectorTest.php index add100d47b7df..18269d28e7339 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/ConfigDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/ConfigDataCollectorTest.php @@ -36,6 +36,7 @@ public function testCollect() $this->assertSame(class_exists('Locale', false) && \Locale::getDefault() ? \Locale::getDefault() : 'n/a', $c->getPhpIntlLocale()); $this->assertSame(date_default_timezone_get(), $c->getPhpTimezone()); $this->assertSame(Kernel::VERSION, $c->getSymfonyVersion()); + $this->assertSame(4 === Kernel::MINOR_VERSION, $c->isSymfonyLts()); $this->assertNull($c->getToken()); $this->assertSame(\extension_loaded('xdebug'), $c->hasXDebug()); $this->assertSame(\extension_loaded('Zend OPcache') && filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN), $c->hasZendOpcache()); diff --git a/src/Symfony/Component/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json index e07b453b48113..4d923b716664c 100644 --- a/src/Symfony/Component/HttpKernel/composer.json +++ b/src/Symfony/Component/HttpKernel/composer.json @@ -18,28 +18,28 @@ "require": { "php": "^7.1.3", "symfony/event-dispatcher": "^4.3", - "symfony/http-foundation": "^4.1.1", - "symfony/debug": "~3.4|~4.0", - "symfony/polyfill-ctype": "~1.8", + "symfony/http-foundation": "^4.4|^5.0", + "symfony/debug": "^3.4|^4.0|^5.0", + "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-php73": "^1.9", "psr/log": "~1.0" }, "require-dev": { - "symfony/browser-kit": "^4.3", - "symfony/config": "~3.4|~4.0", - "symfony/console": "~3.4|~4.0", - "symfony/css-selector": "~3.4|~4.0", - "symfony/dependency-injection": "^4.3", - "symfony/dom-crawler": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/finder": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0", - "symfony/routing": "~3.4|~4.0", - "symfony/stopwatch": "~3.4|~4.0", - "symfony/templating": "~3.4|~4.0", - "symfony/translation": "~4.2", + "symfony/browser-kit": "^4.3|^5.0", + "symfony/config": "^3.4|^4.0|^5.0", + "symfony/console": "^3.4|^4.0", + "symfony/css-selector": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^4.3|^5.0", + "symfony/dom-crawler": "^3.4|^4.0|^5.0", + "symfony/expression-language": "^3.4|^4.0|^5.0", + "symfony/finder": "^3.4|^4.0|^5.0", + "symfony/process": "^3.4|^4.0|^5.0", + "symfony/routing": "^3.4|^4.0|^5.0", + "symfony/stopwatch": "^3.4|^4.0|^5.0", + "symfony/templating": "^3.4|^4.0|^5.0", + "symfony/translation": "^4.2|^5.0", "symfony/translation-contracts": "^1.1", - "symfony/var-dumper": "^4.1.1", + "symfony/var-dumper": "^4.1.1|^5.0", "psr/cache": "~1.0", "twig/twig": "^1.34|^2.4" }, @@ -49,6 +49,7 @@ "conflict": { "symfony/browser-kit": "<4.3", "symfony/config": "<3.4", + "symfony/console": ">=5", "symfony/dependency-injection": "<4.3", "symfony/translation": "<4.2", "symfony/var-dumper": "<4.1.1", @@ -70,7 +71,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Inflector/composer.json b/src/Symfony/Component/Inflector/composer.json index 2a4e29695d85f..afd56dfd6bd32 100644 --- a/src/Symfony/Component/Inflector/composer.json +++ b/src/Symfony/Component/Inflector/composer.json @@ -35,7 +35,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Intl/composer.json b/src/Symfony/Component/Intl/composer.json index 64a2ebf1a64d7..291b53684114e 100644 --- a/src/Symfony/Component/Intl/composer.json +++ b/src/Symfony/Component/Intl/composer.json @@ -28,7 +28,7 @@ "symfony/polyfill-intl-icu": "~1.0" }, "require-dev": { - "symfony/filesystem": "~3.4|~4.0" + "symfony/filesystem": "^3.4|^4.0|^5.0" }, "suggest": { "ext-intl": "to use the component with locales other than \"en\"" @@ -43,7 +43,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php index 8ba19ee731370..3c6262c467ade 100644 --- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php +++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php @@ -12,7 +12,10 @@ namespace Symfony\Component\Ldap\Adapter\ExtLdap; use Symfony\Component\Ldap\Adapter\AbstractConnection; +use Symfony\Component\Ldap\Exception\AlreadyExistsException; use Symfony\Component\Ldap\Exception\ConnectionException; +use Symfony\Component\Ldap\Exception\ConnectionTimeoutException; +use Symfony\Component\Ldap\Exception\InvalidCredentialsException; use Symfony\Component\Ldap\Exception\LdapException; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -22,6 +25,10 @@ */ class Connection extends AbstractConnection { + private const LDAP_INVALID_CREDENTIALS = '0x31'; + private const LDAP_TIMEOUT = '0x55'; + private const LDAP_ALREADY_EXISTS = '0x44'; + /** @var bool */ private $bound = false; @@ -51,7 +58,16 @@ public function bind($dn = null, $password = null) } if (false === @ldap_bind($this->connection, $dn, $password)) { - throw new ConnectionException(ldap_error($this->connection)); + $error = ldap_error($this->connection); + switch (ldap_errno($this->connection)) { + case self::LDAP_INVALID_CREDENTIALS: + throw new InvalidCredentialsException($error); + case self::LDAP_TIMEOUT: + throw new ConnectionTimeoutException($error); + case self::LDAP_ALREADY_EXISTS: + throw new AlreadyExistsException($error); + } + throw new ConnectionException($error); } $this->bound = true; diff --git a/src/Symfony/Component/Ldap/CHANGELOG.md b/src/Symfony/Component/Ldap/CHANGELOG.md index ca2d18fad2e0f..fbfe926ac4e0e 100644 --- a/src/Symfony/Component/Ldap/CHANGELOG.md +++ b/src/Symfony/Component/Ldap/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +4.4.0 +----- + + * Added the "extra_fields" option, an array of custom fields to pull from the LDAP server + 4.3.0 ----- diff --git a/src/Symfony/Component/Ldap/Exception/AlreadyExistsException.php b/src/Symfony/Component/Ldap/Exception/AlreadyExistsException.php new file mode 100644 index 0000000000000..51635037ac26a --- /dev/null +++ b/src/Symfony/Component/Ldap/Exception/AlreadyExistsException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Ldap\Exception; + +/** + * AlreadyExistsException is thrown if the element already exists. + * + * @author Hamza Amrouche + */ +class AlreadyExistsException extends ConnectionException implements ExceptionInterface +{ +} diff --git a/src/Symfony/Component/Ldap/Exception/ConnectionTimeoutException.php b/src/Symfony/Component/Ldap/Exception/ConnectionTimeoutException.php new file mode 100644 index 0000000000000..41533412ddb87 --- /dev/null +++ b/src/Symfony/Component/Ldap/Exception/ConnectionTimeoutException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Ldap\Exception; + +/** + * ConnectionException is thrown if binding to ldap time out. + * + * @author Hamza Amrouche + */ +class ConnectionTimeoutException extends ConnectionException implements ExceptionInterface +{ +} diff --git a/src/Symfony/Component/Ldap/Exception/InvalidCredentialsException.php b/src/Symfony/Component/Ldap/Exception/InvalidCredentialsException.php new file mode 100644 index 0000000000000..b5cffce9e9c0c --- /dev/null +++ b/src/Symfony/Component/Ldap/Exception/InvalidCredentialsException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Ldap\Exception; + +/** + * ConnectionException is thrown if binding to ldap has been done with invalid credentials . + * + * @author Hamza Amrouche + */ +class InvalidCredentialsException extends ConnectionException implements ExceptionInterface +{ +} diff --git a/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/LdapManagerTest.php b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/LdapManagerTest.php index 7b557eee7ef6b..bca5b0ec8f964 100644 --- a/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/LdapManagerTest.php +++ b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/LdapManagerTest.php @@ -15,6 +15,7 @@ use Symfony\Component\Ldap\Adapter\ExtLdap\Collection; use Symfony\Component\Ldap\Adapter\ExtLdap\UpdateOperation; use Symfony\Component\Ldap\Entry; +use Symfony\Component\Ldap\Exception\AlreadyExistsException; use Symfony\Component\Ldap\Exception\LdapException; use Symfony\Component\Ldap\Exception\NotBoundException; use Symfony\Component\Ldap\Exception\UpdateOperationException; @@ -75,6 +76,26 @@ public function testLdapAddInvalidEntry() $em->add($entry); } + /** + * @group functional + */ + public function testLdapAddDouble() + { + $this->expectException(AlreadyExistsException::class); + $this->executeSearchQuery(1); + + $entry = new Entry('cn=Elsa Amrouche,dc=symfony,dc=com', [ + 'sn' => ['eamrouche'], + 'objectclass' => [ + 'inetOrgPerson', + ], + ]); + + $em = $this->adapter->getEntryManager(); + $em->add($entry); + $em->add($entry); + } + /** * @group functional */ diff --git a/src/Symfony/Component/Ldap/composer.json b/src/Symfony/Component/Ldap/composer.json index e8fb2720f4d12..c302be83ec7a2 100644 --- a/src/Symfony/Component/Ldap/composer.json +++ b/src/Symfony/Component/Ldap/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": "^7.1.3", - "symfony/options-resolver": "~4.2", + "symfony/options-resolver": "^4.2|^5.0", "ext-ldap": "*" }, "conflict": { @@ -32,7 +32,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Lock/composer.json b/src/Symfony/Component/Lock/composer.json index 3e8e77e5a3819..5909a421eacfc 100644 --- a/src/Symfony/Component/Lock/composer.json +++ b/src/Symfony/Component/Lock/composer.json @@ -33,7 +33,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Mailer/Bridge/Amazon/Http/Api/SesTransport.php b/src/Symfony/Component/Mailer/Bridge/Amazon/Http/Api/SesTransport.php index c10b8df3ae90d..1ed145bcbad32 100644 --- a/src/Symfony/Component/Mailer/Bridge/Amazon/Http/Api/SesTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Amazon/Http/Api/SesTransport.php @@ -12,11 +12,11 @@ namespace Symfony\Component\Mailer\Bridge\Amazon\Http\Api; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Mailer\Exception\TransportException; use Symfony\Component\Mailer\SmtpEnvelope; use Symfony\Component\Mailer\Transport\Http\Api\AbstractApiTransport; use Symfony\Component\Mime\Email; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; /** diff --git a/src/Symfony/Component/Mailer/Bridge/Amazon/Http/SesTransport.php b/src/Symfony/Component/Mailer/Bridge/Amazon/Http/SesTransport.php index edca32c58fb3a..c27a0d1eb2892 100644 --- a/src/Symfony/Component/Mailer/Bridge/Amazon/Http/SesTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Amazon/Http/SesTransport.php @@ -12,10 +12,10 @@ namespace Symfony\Component\Mailer\Bridge\Amazon\Http; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Mailer\Exception\TransportException; use Symfony\Component\Mailer\SentMessage; use Symfony\Component\Mailer\Transport\Http\AbstractHttpTransport; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; /** diff --git a/src/Symfony/Component/Mailer/Bridge/Amazon/Smtp/SesTransport.php b/src/Symfony/Component/Mailer/Bridge/Amazon/Smtp/SesTransport.php index e17de68f8e33d..9b11096a19396 100644 --- a/src/Symfony/Component/Mailer/Bridge/Amazon/Smtp/SesTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Amazon/Smtp/SesTransport.php @@ -12,8 +12,8 @@ namespace Symfony\Component\Mailer\Bridge\Amazon\Smtp; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** * @author Kevin Verschaeve diff --git a/src/Symfony/Component/Mailer/Bridge/Amazon/composer.json b/src/Symfony/Component/Mailer/Bridge/Amazon/composer.json index bda7c65123a5d..b0fd9da26a605 100644 --- a/src/Symfony/Component/Mailer/Bridge/Amazon/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Amazon/composer.json @@ -17,10 +17,10 @@ ], "require": { "php": "^7.1.3", - "symfony/mailer": "^4.3" + "symfony/mailer": "^4.4|^5.0" }, "require-dev": { - "symfony/http-client": "^4.3" + "symfony/http-client": "^4.3|^5.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Mailer\\Bridge\\Amazon\\": "" }, @@ -31,7 +31,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Mailer/Bridge/Google/Smtp/GmailTransport.php b/src/Symfony/Component/Mailer/Bridge/Google/Smtp/GmailTransport.php index fb7f58264748a..0c651c86c626a 100644 --- a/src/Symfony/Component/Mailer/Bridge/Google/Smtp/GmailTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Google/Smtp/GmailTransport.php @@ -12,8 +12,8 @@ namespace Symfony\Component\Mailer\Bridge\Google\Smtp; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** * @author Kevin Verschaeve diff --git a/src/Symfony/Component/Mailer/Bridge/Google/composer.json b/src/Symfony/Component/Mailer/Bridge/Google/composer.json index bca36a66feaa4..ea7fd9a7ab426 100644 --- a/src/Symfony/Component/Mailer/Bridge/Google/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Google/composer.json @@ -17,10 +17,10 @@ ], "require": { "php": "^7.1.3", - "symfony/mailer": "^4.3" + "symfony/mailer": "^4.4|^5.0" }, "require-dev": { - "symfony/http-client": "^4.3" + "symfony/http-client": "^4.3|^5.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Mailer\\Bridge\\Google\\": "" }, @@ -31,7 +31,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Http/Api/MandrillTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Http/Api/MandrillTransport.php index a177e664b62a2..851f64a2fc49b 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Http/Api/MandrillTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Http/Api/MandrillTransport.php @@ -12,11 +12,11 @@ namespace Symfony\Component\Mailer\Bridge\Mailchimp\Http\Api; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Mailer\Exception\TransportException; use Symfony\Component\Mailer\SmtpEnvelope; use Symfony\Component\Mailer\Transport\Http\Api\AbstractApiTransport; use Symfony\Component\Mime\Email; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; /** diff --git a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Http/MandrillTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Http/MandrillTransport.php index ea8bcf4dbbba5..54a88b3720ad8 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Http/MandrillTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Http/MandrillTransport.php @@ -12,10 +12,10 @@ namespace Symfony\Component\Mailer\Bridge\Mailchimp\Http; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Mailer\Exception\TransportException; use Symfony\Component\Mailer\SentMessage; use Symfony\Component\Mailer\Transport\Http\AbstractHttpTransport; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; /** diff --git a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Smtp/MandrillTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Smtp/MandrillTransport.php index 75c665f3cc128..e93492e370b1f 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Smtp/MandrillTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Smtp/MandrillTransport.php @@ -12,8 +12,8 @@ namespace Symfony\Component\Mailer\Bridge\Mailchimp\Smtp; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** * @author Kevin Verschaeve diff --git a/src/Symfony/Component/Mailer/Bridge/Mailchimp/composer.json b/src/Symfony/Component/Mailer/Bridge/Mailchimp/composer.json index 761ec6989a0a8..569d39f2ec855 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailchimp/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Mailchimp/composer.json @@ -17,10 +17,10 @@ ], "require": { "php": "^7.1.3", - "symfony/mailer": "^4.3" + "symfony/mailer": "^4.4|^5.0" }, "require-dev": { - "symfony/http-client": "^4.3" + "symfony/http-client": "^4.3|^5.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Mailer\\Bridge\\Mailchimp\\": "" }, @@ -31,7 +31,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Mailer/Bridge/Mailgun/Http/Api/MailgunTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailgun/Http/Api/MailgunTransport.php index f546537831e85..0740050d2d356 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailgun/Http/Api/MailgunTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Mailgun/Http/Api/MailgunTransport.php @@ -12,12 +12,12 @@ namespace Symfony\Component\Mailer\Bridge\Mailgun\Http\Api; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Mailer\Exception\TransportException; use Symfony\Component\Mailer\SmtpEnvelope; use Symfony\Component\Mailer\Transport\Http\Api\AbstractApiTransport; use Symfony\Component\Mime\Email; use Symfony\Component\Mime\Part\Multipart\FormDataPart; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; /** diff --git a/src/Symfony/Component/Mailer/Bridge/Mailgun/Http/MailgunTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailgun/Http/MailgunTransport.php index 913ed705d9ab0..cd7b2254ec38c 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailgun/Http/MailgunTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Mailgun/Http/MailgunTransport.php @@ -12,12 +12,12 @@ namespace Symfony\Component\Mailer\Bridge\Mailgun\Http; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Mailer\Exception\TransportException; use Symfony\Component\Mailer\SentMessage; use Symfony\Component\Mailer\Transport\Http\AbstractHttpTransport; use Symfony\Component\Mime\Part\DataPart; use Symfony\Component\Mime\Part\Multipart\FormDataPart; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; /** diff --git a/src/Symfony/Component/Mailer/Bridge/Mailgun/Smtp/MailgunTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailgun/Smtp/MailgunTransport.php index c288a46bdd73a..fa68465d12553 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailgun/Smtp/MailgunTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Mailgun/Smtp/MailgunTransport.php @@ -12,8 +12,8 @@ namespace Symfony\Component\Mailer\Bridge\Mailgun\Smtp; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** * @author Kevin Verschaeve diff --git a/src/Symfony/Component/Mailer/Bridge/Mailgun/composer.json b/src/Symfony/Component/Mailer/Bridge/Mailgun/composer.json index 6f00d507ebe60..af7fa9b836916 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailgun/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Mailgun/composer.json @@ -17,10 +17,10 @@ ], "require": { "php": "^7.1.3", - "symfony/mailer": "^4.3" + "symfony/mailer": "^4.4|^5.0" }, "require-dev": { - "symfony/http-client": "^4.3" + "symfony/http-client": "^4.3|^5.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Mailer\\Bridge\\Mailgun\\": "" }, @@ -31,7 +31,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Mailer/Bridge/Postmark/Http/Api/PostmarkTransport.php b/src/Symfony/Component/Mailer/Bridge/Postmark/Http/Api/PostmarkTransport.php index 3ec9c640a655d..9c1af02c84397 100644 --- a/src/Symfony/Component/Mailer/Bridge/Postmark/Http/Api/PostmarkTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Postmark/Http/Api/PostmarkTransport.php @@ -12,11 +12,11 @@ namespace Symfony\Component\Mailer\Bridge\Postmark\Http\Api; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Mailer\Exception\TransportException; use Symfony\Component\Mailer\SmtpEnvelope; use Symfony\Component\Mailer\Transport\Http\Api\AbstractApiTransport; use Symfony\Component\Mime\Email; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; /** diff --git a/src/Symfony/Component/Mailer/Bridge/Postmark/Smtp/PostmarkTransport.php b/src/Symfony/Component/Mailer/Bridge/Postmark/Smtp/PostmarkTransport.php index 4407a1bf1b0af..9e0fbe3479bae 100644 --- a/src/Symfony/Component/Mailer/Bridge/Postmark/Smtp/PostmarkTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Postmark/Smtp/PostmarkTransport.php @@ -12,8 +12,8 @@ namespace Symfony\Component\Mailer\Bridge\Postmark\Smtp; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** * @author Kevin Verschaeve diff --git a/src/Symfony/Component/Mailer/Bridge/Postmark/composer.json b/src/Symfony/Component/Mailer/Bridge/Postmark/composer.json index 0493f1dfb0853..572c27bf57b06 100644 --- a/src/Symfony/Component/Mailer/Bridge/Postmark/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Postmark/composer.json @@ -17,10 +17,10 @@ ], "require": { "php": "^7.1.3", - "symfony/mailer": "^4.3" + "symfony/mailer": "^4.4|^5.0" }, "require-dev": { - "symfony/http-client": "^4.3" + "symfony/http-client": "^4.3|^5.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Mailer\\Bridge\\Postmark\\": "" }, @@ -31,7 +31,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Mailer/Bridge/Sendgrid/Http/Api/SendgridTransport.php b/src/Symfony/Component/Mailer/Bridge/Sendgrid/Http/Api/SendgridTransport.php index 8369d4e2775a0..b1678da346ccb 100644 --- a/src/Symfony/Component/Mailer/Bridge/Sendgrid/Http/Api/SendgridTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Sendgrid/Http/Api/SendgridTransport.php @@ -12,12 +12,12 @@ namespace Symfony\Component\Mailer\Bridge\Sendgrid\Http\Api; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Mailer\Exception\TransportException; use Symfony\Component\Mailer\SmtpEnvelope; use Symfony\Component\Mailer\Transport\Http\Api\AbstractApiTransport; use Symfony\Component\Mime\Address; use Symfony\Component\Mime\Email; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; /** diff --git a/src/Symfony/Component/Mailer/Bridge/Sendgrid/Smtp/SendgridTransport.php b/src/Symfony/Component/Mailer/Bridge/Sendgrid/Smtp/SendgridTransport.php index 60ef601a16a62..6b9c74ca80249 100644 --- a/src/Symfony/Component/Mailer/Bridge/Sendgrid/Smtp/SendgridTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Sendgrid/Smtp/SendgridTransport.php @@ -12,8 +12,8 @@ namespace Symfony\Component\Mailer\Bridge\Sendgrid\Smtp; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** * @author Kevin Verschaeve diff --git a/src/Symfony/Component/Mailer/Bridge/Sendgrid/composer.json b/src/Symfony/Component/Mailer/Bridge/Sendgrid/composer.json index 5630f5d3f40f8..bd7fae77dda09 100644 --- a/src/Symfony/Component/Mailer/Bridge/Sendgrid/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Sendgrid/composer.json @@ -17,10 +17,10 @@ ], "require": { "php": "^7.1.3", - "symfony/mailer": "^4.3" + "symfony/mailer": "^4.4|^5.0" }, "require-dev": { - "symfony/http-client": "^4.3" + "symfony/http-client": "^4.3|^5.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Mailer\\Bridge\\Sendgrid\\": "" }, @@ -31,7 +31,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Mailer/CHANGELOG.md b/src/Symfony/Component/Mailer/CHANGELOG.md index 086e3305a7eb8..5b2c5c528fca5 100644 --- a/src/Symfony/Component/Mailer/CHANGELOG.md +++ b/src/Symfony/Component/Mailer/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +4.4.0 +----- + + * [BC BREAK] Transports depend on `Symfony\Contracts\EventDispatcher\EventDispatcherInterface` + instead of `Symfony\Component\EventDispatcher\EventDispatcherInterface`. + 4.3.0 ----- diff --git a/src/Symfony/Component/Mailer/Tests/TransportTest.php b/src/Symfony/Component/Mailer/Tests/TransportTest.php index a262744472a89..8c7cd99d98960 100644 --- a/src/Symfony/Component/Mailer/Tests/TransportTest.php +++ b/src/Symfony/Component/Mailer/Tests/TransportTest.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Mailer\Bridge\Amazon; use Symfony\Component\Mailer\Bridge\Google; use Symfony\Component\Mailer\Bridge\Mailchimp; @@ -24,6 +23,7 @@ use Symfony\Component\Mailer\Exception\LogicException; use Symfony\Component\Mailer\Transport; use Symfony\Component\Mime\Email; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\ResponseInterface; diff --git a/src/Symfony/Component/Mailer/Transport.php b/src/Symfony/Component/Mailer/Transport.php index 46d9397067440..9c703f51dff1f 100644 --- a/src/Symfony/Component/Mailer/Transport.php +++ b/src/Symfony/Component/Mailer/Transport.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Mailer; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Mailer\Bridge\Amazon; use Symfony\Component\Mailer\Bridge\Google; use Symfony\Component\Mailer\Bridge\Mailchimp; @@ -22,6 +21,7 @@ use Symfony\Component\Mailer\Exception\InvalidArgumentException; use Symfony\Component\Mailer\Exception\LogicException; use Symfony\Component\Mailer\Transport\TransportInterface; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; /** diff --git a/src/Symfony/Component/Mailer/Transport/AbstractTransport.php b/src/Symfony/Component/Mailer/Transport/AbstractTransport.php index 2b1fd87b748e8..d4453bddb0cad 100644 --- a/src/Symfony/Component/Mailer/Transport/AbstractTransport.php +++ b/src/Symfony/Component/Mailer/Transport/AbstractTransport.php @@ -14,7 +14,6 @@ use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; use Symfony\Component\EventDispatcher\EventDispatcher; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Mailer\DelayedSmtpEnvelope; use Symfony\Component\Mailer\Event\MessageEvent; use Symfony\Component\Mailer\Exception\TransportException; @@ -22,6 +21,7 @@ use Symfony\Component\Mailer\SmtpEnvelope; use Symfony\Component\Mime\Address; use Symfony\Component\Mime\RawMessage; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** * @author Fabien Potencier diff --git a/src/Symfony/Component/Mailer/Transport/Http/AbstractHttpTransport.php b/src/Symfony/Component/Mailer/Transport/Http/AbstractHttpTransport.php index f431c2fe8530b..c350a581389c2 100644 --- a/src/Symfony/Component/Mailer/Transport/Http/AbstractHttpTransport.php +++ b/src/Symfony/Component/Mailer/Transport/Http/AbstractHttpTransport.php @@ -12,9 +12,9 @@ namespace Symfony\Component\Mailer\Transport\Http; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpClient\HttpClient; use Symfony\Component\Mailer\Transport\AbstractTransport; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; /** diff --git a/src/Symfony/Component/Mailer/Transport/Http/Api/AbstractApiTransport.php b/src/Symfony/Component/Mailer/Transport/Http/Api/AbstractApiTransport.php index f844267fede15..3364051f073ef 100644 --- a/src/Symfony/Component/Mailer/Transport/Http/Api/AbstractApiTransport.php +++ b/src/Symfony/Component/Mailer/Transport/Http/Api/AbstractApiTransport.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Mailer\Transport\Http\Api; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpClient\HttpClient; use Symfony\Component\Mailer\Exception\RuntimeException; use Symfony\Component\Mailer\SentMessage; @@ -21,6 +20,7 @@ use Symfony\Component\Mime\Address; use Symfony\Component\Mime\Email; use Symfony\Component\Mime\MessageConverter; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; /** diff --git a/src/Symfony/Component/Mailer/Transport/SendmailTransport.php b/src/Symfony/Component/Mailer/Transport/SendmailTransport.php index b8b4512a3603c..732e336735833 100644 --- a/src/Symfony/Component/Mailer/Transport/SendmailTransport.php +++ b/src/Symfony/Component/Mailer/Transport/SendmailTransport.php @@ -12,13 +12,13 @@ namespace Symfony\Component\Mailer\Transport; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Mailer\SentMessage; use Symfony\Component\Mailer\SmtpEnvelope; use Symfony\Component\Mailer\Transport\Smtp\SmtpTransport; use Symfony\Component\Mailer\Transport\Smtp\Stream\AbstractStream; use Symfony\Component\Mailer\Transport\Smtp\Stream\ProcessStream; use Symfony\Component\Mime\RawMessage; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** * SendmailTransport for sending mail through a Sendmail/Postfix (etc..) binary. diff --git a/src/Symfony/Component/Mailer/Transport/Smtp/EsmtpTransport.php b/src/Symfony/Component/Mailer/Transport/Smtp/EsmtpTransport.php index d3a93debd1dc5..6db5b1f800038 100644 --- a/src/Symfony/Component/Mailer/Transport/Smtp/EsmtpTransport.php +++ b/src/Symfony/Component/Mailer/Transport/Smtp/EsmtpTransport.php @@ -12,11 +12,11 @@ namespace Symfony\Component\Mailer\Transport\Smtp; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Mailer\Exception\TransportException; use Symfony\Component\Mailer\Exception\TransportExceptionInterface; use Symfony\Component\Mailer\Transport\Smtp\Auth\AuthenticatorInterface; use Symfony\Component\Mailer\Transport\Smtp\Stream\SocketStream; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** * Sends Emails over SMTP with ESMTP support. diff --git a/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php b/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php index 05bbc0df59b6f..50e466fe41254 100644 --- a/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php +++ b/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Mailer\Transport\Smtp; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Mailer\Exception\LogicException; use Symfony\Component\Mailer\Exception\TransportException; use Symfony\Component\Mailer\Exception\TransportExceptionInterface; @@ -22,6 +21,7 @@ use Symfony\Component\Mailer\Transport\Smtp\Stream\AbstractStream; use Symfony\Component\Mailer\Transport\Smtp\Stream\SocketStream; use Symfony\Component\Mime\RawMessage; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** * Sends emails over SMTP. diff --git a/src/Symfony/Component/Mailer/composer.json b/src/Symfony/Component/Mailer/composer.json index 91bfb1031d937..31a9360564d91 100644 --- a/src/Symfony/Component/Mailer/composer.json +++ b/src/Symfony/Component/Mailer/composer.json @@ -20,16 +20,16 @@ "egulias/email-validator": "^2.0", "psr/log": "~1.0", "symfony/event-dispatcher": "^4.3", - "symfony/mime": "^4.3" + "symfony/mime": "^4.3|^5.0" }, "require-dev": { - "symfony/amazon-mailer": "^4.3", - "symfony/google-mailer": "^4.3", + "symfony/amazon-mailer": "^4.4|^5.0", + "symfony/google-mailer": "^4.4|^5.0", "symfony/http-client-contracts": "^1.1", - "symfony/mailgun-mailer": "^4.3.2", - "symfony/mailchimp-mailer": "^4.3", - "symfony/postmark-mailer": "^4.3", - "symfony/sendgrid-mailer": "^4.3" + "symfony/mailgun-mailer": "^4.4|^5.0", + "symfony/mailchimp-mailer": "^4.4|^5.0", + "symfony/postmark-mailer": "^4.4|^5.0", + "symfony/sendgrid-mailer": "^4.4|^5.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Mailer\\": "" }, @@ -40,7 +40,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Messenger/CHANGELOG.md b/src/Symfony/Component/Messenger/CHANGELOG.md index 49d04feb1f276..fc01fa52ccff2 100644 --- a/src/Symfony/Component/Messenger/CHANGELOG.md +++ b/src/Symfony/Component/Messenger/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +4.4.0 +----- + + * Deprecated passing a `ContainerInterface` instance as first argument of the `ConsumeMessagesCommand` constructor, + pass a `RoutableMessageBus` instance instead. + 4.3.0 ----- diff --git a/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php b/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php index be6f4c1733b27..816cd30c3562b 100644 --- a/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php +++ b/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php @@ -54,8 +54,8 @@ class ConsumeMessagesCommand extends Command */ public function __construct($routableBus, ContainerInterface $receiverLocator, LoggerInterface $logger = null, array $receiverNames = [], /* ContainerInterface */ $retryStrategyLocator = null, EventDispatcherInterface $eventDispatcher = null) { - // to be deprecated in 4.4 if ($routableBus instanceof ContainerInterface) { + @trigger_error(sprintf('Passing a "%s" instance as first argument to "%s()" is deprecated since Symfony 4.4, pass a "%s" instance instead.', ContainerInterface::class, __METHOD__, RoutableMessageBus::class), E_USER_DEPRECATED); $routableBus = new RoutableMessageBus($routableBus); } diff --git a/src/Symfony/Component/Messenger/Stamp/BusNameStamp.php b/src/Symfony/Component/Messenger/Stamp/BusNameStamp.php index eee3f9e8465fa..61c721458d35a 100644 --- a/src/Symfony/Component/Messenger/Stamp/BusNameStamp.php +++ b/src/Symfony/Component/Messenger/Stamp/BusNameStamp.php @@ -18,7 +18,7 @@ * * @author Ryan Weaver */ -class BusNameStamp implements StampInterface +final class BusNameStamp implements StampInterface { private $busName; diff --git a/src/Symfony/Component/Messenger/Stamp/DelayStamp.php b/src/Symfony/Component/Messenger/Stamp/DelayStamp.php index 0fc5597044e47..7badd7a4f3023 100644 --- a/src/Symfony/Component/Messenger/Stamp/DelayStamp.php +++ b/src/Symfony/Component/Messenger/Stamp/DelayStamp.php @@ -16,7 +16,7 @@ * * @experimental in 4.3 */ -class DelayStamp implements StampInterface +final class DelayStamp implements StampInterface { private $delay; diff --git a/src/Symfony/Component/Messenger/Stamp/DispatchAfterCurrentBusStamp.php b/src/Symfony/Component/Messenger/Stamp/DispatchAfterCurrentBusStamp.php index 38222cbc3b76e..a166801f90ff0 100644 --- a/src/Symfony/Component/Messenger/Stamp/DispatchAfterCurrentBusStamp.php +++ b/src/Symfony/Component/Messenger/Stamp/DispatchAfterCurrentBusStamp.php @@ -20,6 +20,6 @@ * * @author Tobias Nyholm */ -class DispatchAfterCurrentBusStamp implements StampInterface +final class DispatchAfterCurrentBusStamp implements NonSendableStampInterface { } diff --git a/src/Symfony/Component/Messenger/Stamp/HandledStamp.php b/src/Symfony/Component/Messenger/Stamp/HandledStamp.php index 2002d08fddb50..d890e5f21f587 100644 --- a/src/Symfony/Component/Messenger/Stamp/HandledStamp.php +++ b/src/Symfony/Component/Messenger/Stamp/HandledStamp.php @@ -17,7 +17,11 @@ * Stamp identifying a message handled by the `HandleMessageMiddleware` middleware * and storing the handler returned value. * + * This is used by synchronous command buses expecting a return value and the retry logic + * to only execute handlers that didn't succeed. + * * @see \Symfony\Component\Messenger\Middleware\HandleMessageMiddleware + * @see \Symfony\Component\Messenger\HandleTrait * * @author Maxime Steinhausser * diff --git a/src/Symfony/Component/Messenger/Stamp/ReceivedStamp.php b/src/Symfony/Component/Messenger/Stamp/ReceivedStamp.php index 3296cde1d316c..d0e2eae13fccb 100644 --- a/src/Symfony/Component/Messenger/Stamp/ReceivedStamp.php +++ b/src/Symfony/Component/Messenger/Stamp/ReceivedStamp.php @@ -25,7 +25,7 @@ * * @experimental in 4.3 */ -final class ReceivedStamp implements StampInterface +final class ReceivedStamp implements NonSendableStampInterface { private $transportName; diff --git a/src/Symfony/Component/Messenger/Stamp/RedeliveryStamp.php b/src/Symfony/Component/Messenger/Stamp/RedeliveryStamp.php index 2895ad9b8325a..83ce1a3558794 100644 --- a/src/Symfony/Component/Messenger/Stamp/RedeliveryStamp.php +++ b/src/Symfony/Component/Messenger/Stamp/RedeliveryStamp.php @@ -19,7 +19,7 @@ * * @experimental in 4.3 */ -class RedeliveryStamp implements StampInterface +final class RedeliveryStamp implements StampInterface { private $retryCount; private $senderClassOrAlias; diff --git a/src/Symfony/Component/Messenger/Stamp/SentStamp.php b/src/Symfony/Component/Messenger/Stamp/SentStamp.php index 3f1a8f718661a..0aec68c74eaa2 100644 --- a/src/Symfony/Component/Messenger/Stamp/SentStamp.php +++ b/src/Symfony/Component/Messenger/Stamp/SentStamp.php @@ -20,7 +20,7 @@ * * @experimental in 4.3 */ -final class SentStamp implements StampInterface +final class SentStamp implements NonSendableStampInterface { private $senderClass; private $senderAlias; diff --git a/src/Symfony/Component/Messenger/Stamp/SentToFailureTransportStamp.php b/src/Symfony/Component/Messenger/Stamp/SentToFailureTransportStamp.php index c11574f6e7134..32bdf8a30191d 100644 --- a/src/Symfony/Component/Messenger/Stamp/SentToFailureTransportStamp.php +++ b/src/Symfony/Component/Messenger/Stamp/SentToFailureTransportStamp.php @@ -18,7 +18,7 @@ * * @experimental in 4.3 */ -class SentToFailureTransportStamp implements StampInterface +final class SentToFailureTransportStamp implements StampInterface { private $originalReceiverName; diff --git a/src/Symfony/Component/Messenger/Stamp/TransportMessageIdStamp.php b/src/Symfony/Component/Messenger/Stamp/TransportMessageIdStamp.php index b0b93ae1885c3..603021ec08f32 100644 --- a/src/Symfony/Component/Messenger/Stamp/TransportMessageIdStamp.php +++ b/src/Symfony/Component/Messenger/Stamp/TransportMessageIdStamp.php @@ -18,7 +18,7 @@ * * @experimental in 4.3 */ -class TransportMessageIdStamp implements StampInterface +final class TransportMessageIdStamp implements StampInterface { private $id; diff --git a/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php index 3191c65b644a4..c7ae75cc1b428 100644 --- a/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php +++ b/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php @@ -27,7 +27,7 @@ class ConsumeMessagesCommandTest extends TestCase { public function testConfigurationWithDefaultReceiver() { - $command = new ConsumeMessagesCommand($this->createMock(ServiceLocator::class), $this->createMock(ServiceLocator::class), null, ['amqp']); + $command = new ConsumeMessagesCommand($this->createMock(RoutableMessageBus::class), $this->createMock(ServiceLocator::class), null, ['amqp']); $inputArgument = $command->getDefinition()->getArgument('receivers'); $this->assertFalse($inputArgument->isRequired()); $this->assertSame(['amqp'], $inputArgument->getDefault()); @@ -98,6 +98,10 @@ public function testRunWithBusOption() $this->assertContains('[OK] Consuming messages from transports "dummy-receiver"', $tester->getDisplay()); } + /** + * @group legacy + * @expectedDeprecation Passing a "Psr\Container\ContainerInterface" instance as first argument to "Symfony\Component\Messenger\Command\ConsumeMessagesCommand::__construct()" is deprecated since Symfony 4.4, pass a "Symfony\Component\Messenger\RoutableMessageBus" instance instead. + */ public function testBasicRunWithBusLocator() { $envelope = new Envelope(new \stdClass(), [new BusNameStamp('dummy-bus')]); @@ -130,6 +134,10 @@ public function testBasicRunWithBusLocator() $this->assertContains('[OK] Consuming messages from transports "dummy-receiver"', $tester->getDisplay()); } + /** + * @group legacy + * @expectedDeprecation Passing a "Psr\Container\ContainerInterface" instance as first argument to "Symfony\Component\Messenger\Command\ConsumeMessagesCommand::__construct()" is deprecated since Symfony 4.4, pass a "Symfony\Component\Messenger\RoutableMessageBus" instance instead. + */ public function testRunWithBusOptionAndBusLocator() { $envelope = new Envelope(new \stdClass()); diff --git a/src/Symfony/Component/Messenger/Tests/EnvelopeTest.php b/src/Symfony/Component/Messenger/Tests/EnvelopeTest.php index eb99c0a3b0d49..ed4d2d4b4c35d 100644 --- a/src/Symfony/Component/Messenger/Tests/EnvelopeTest.php +++ b/src/Symfony/Component/Messenger/Tests/EnvelopeTest.php @@ -55,9 +55,8 @@ public function testWithoutStampsOfType() { $envelope = new Envelope(new DummyMessage('dummy'), [ new ReceivedStamp('transport1'), - new DelayStamp(5000), - new DummyExtendsDelayStamp(5000), - new DummyImplementsFooBarStampInterface(), + new DummyExtendsFooBarStamp(), + new DummyImplementsFooBarStamp(), ]); $envelope2 = $envelope->withoutStampsOfType(DummyNothingImplementsMeStampInterface::class); @@ -66,12 +65,11 @@ public function testWithoutStampsOfType() $envelope3 = $envelope2->withoutStampsOfType(ReceivedStamp::class); $this->assertEmpty($envelope3->all(ReceivedStamp::class)); - $envelope4 = $envelope3->withoutStampsOfType(DelayStamp::class); - $this->assertEmpty($envelope4->all(DelayStamp::class)); - $this->assertEmpty($envelope4->all(DummyExtendsDelayStamp::class)); + $envelope4 = $envelope3->withoutStampsOfType(DummyImplementsFooBarStamp::class); + $this->assertEmpty($envelope4->all(DummyImplementsFooBarStamp::class)); + $this->assertEmpty($envelope4->all(DummyExtendsFooBarStamp::class)); - $envelope5 = $envelope4->withoutStampsOfType(DummyFooBarStampInterface::class); - $this->assertEmpty($envelope5->all(DummyImplementsFooBarStampInterface::class)); + $envelope5 = $envelope3->withoutStampsOfType(DummyFooBarStampInterface::class); $this->assertEmpty($envelope5->all()); } @@ -118,15 +116,15 @@ public function testWrapWithEnvelope() } } -class DummyExtendsDelayStamp extends DelayStamp -{ -} interface DummyFooBarStampInterface extends StampInterface { } interface DummyNothingImplementsMeStampInterface extends StampInterface { } -class DummyImplementsFooBarStampInterface implements DummyFooBarStampInterface +class DummyImplementsFooBarStamp implements DummyFooBarStampInterface +{ +} +class DummyExtendsFooBarStamp extends DummyImplementsFooBarStamp { } diff --git a/src/Symfony/Component/Messenger/composer.json b/src/Symfony/Component/Messenger/composer.json index 798940ef5d36d..cf12d9182fe0c 100644 --- a/src/Symfony/Component/Messenger/composer.json +++ b/src/Symfony/Component/Messenger/composer.json @@ -22,19 +22,19 @@ "require-dev": { "doctrine/dbal": "^2.5", "psr/cache": "~1.0", - "symfony/console": "~3.4|~4.0", - "symfony/debug": "~4.1", - "symfony/dependency-injection": "~3.4.19|^4.1.8", - "symfony/doctrine-bridge": "~3.4|~4.0", - "symfony/event-dispatcher": "~4.3", - "symfony/http-kernel": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0", - "symfony/property-access": "~3.4|~4.0", - "symfony/serializer": "~3.4|~4.0", + "symfony/console": "^3.4|^4.0|^5.0", + "symfony/debug": "^4.1|^5.0", + "symfony/dependency-injection": "^3.4.19|^4.1.8|^5.0", + "symfony/doctrine-bridge": "^3.4|^4.0|^5.0", + "symfony/event-dispatcher": "^4.3|^5.0", + "symfony/http-kernel": "^3.4|^4.0|^5.0", + "symfony/process": "^3.4|^4.0|^5.0", + "symfony/property-access": "^3.4|^4.0|^5.0", + "symfony/serializer": "^3.4|^4.0|^5.0", "symfony/service-contracts": "^1.1", - "symfony/stopwatch": "~3.4|~4.0", - "symfony/validator": "~3.4|~4.0", - "symfony/var-dumper": "~3.4|~4.0" + "symfony/stopwatch": "^3.4|^4.0|^5.0", + "symfony/validator": "^3.4|^4.0|^5.0", + "symfony/var-dumper": "^3.4|^4.0|^5.0" }, "conflict": { "symfony/event-dispatcher": "<4.3", @@ -52,7 +52,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Mime/composer.json b/src/Symfony/Component/Mime/composer.json index f1d2f0975c01d..89c392f55fcf5 100644 --- a/src/Symfony/Component/Mime/composer.json +++ b/src/Symfony/Component/Mime/composer.json @@ -22,7 +22,7 @@ }, "require-dev": { "egulias/email-validator": "^2.0", - "symfony/dependency-injection": "~3.4|^4.1" + "symfony/dependency-injection": "^3.4|^4.1|^5.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Mime\\": "" }, @@ -33,7 +33,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/OptionsResolver/composer.json b/src/Symfony/Component/OptionsResolver/composer.json index 6753856f56b02..d9a237e8ac75a 100644 --- a/src/Symfony/Component/OptionsResolver/composer.json +++ b/src/Symfony/Component/OptionsResolver/composer.json @@ -27,7 +27,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index 5e3993d7882c5..755a574de6b41 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -291,6 +291,12 @@ public function start(callable $callback = null, array $env = []) $this->hasCallback = null !== $callback; $descriptors = $this->getDescriptors(); + if ($this->env) { + $env += $this->env; + } + + $env += $this->getDefaultEnv(); + if (\is_array($commandline = $this->commandline)) { $commandline = implode(' ', array_map([$this, 'escapeArgument'], $commandline)); @@ -298,13 +304,10 @@ public function start(callable $callback = null, array $env = []) // exec is mandatory to deal with sending a signal to the process $commandline = 'exec '.$commandline; } + } else { + $commandline = $this->replacePlaceholders($commandline, $env); } - if ($this->env) { - $env += $this->env; - } - $env += $this->getDefaultEnv(); - $options = ['suppress_errors' => true]; if ('\\' === \DIRECTORY_SEPARATOR) { @@ -1632,6 +1635,17 @@ private function escapeArgument(?string $argument): string return '"'.str_replace(['"', '^', '%', '!', "\n"], ['""', '"^^"', '"^%"', '"^!"', '!LF!'], $argument).'"'; } + private function replacePlaceholders(string $commandline, array $env) + { + return preg_replace_callback('/"\$([_a-zA-Z]++[_a-zA-Z0-9]*+)"/', function ($matches) use ($commandline, $env) { + if (!isset($env[$matches[1]]) || false === $env[$matches[1]]) { + throw new InvalidArgumentException(sprintf('Command line is missing a value for key %s: %s.', $matches[0], $commandline)); + } + + return '\\' === \DIRECTORY_SEPARATOR ? $this->escapeArgument($env[$matches[1]]) : $matches[0]; + }, $commandline); + } + private function getDefaultEnv() { $env = []; diff --git a/src/Symfony/Component/Process/Tests/ProcessTest.php b/src/Symfony/Component/Process/Tests/ProcessTest.php index 8ae8d4ca94f0b..1ee9b3c43dcbf 100644 --- a/src/Symfony/Component/Process/Tests/ProcessTest.php +++ b/src/Symfony/Component/Process/Tests/ProcessTest.php @@ -1500,6 +1500,50 @@ public function provideEscapeArgument() yield [1.1]; } + public function testPreparedCommand() + { + $p = Process::fromShellCommandline('echo "$abc"DEF'); + $p->run(null, ['abc' => 'ABC']); + + $this->assertSame('ABCDEF', rtrim($p->getOutput())); + } + + public function testPreparedCommandMulti() + { + $p = Process::fromShellCommandline('echo "$abc""$def"'); + $p->run(null, ['abc' => 'ABC', 'def' => 'DEF']); + + $this->assertSame('ABCDEF', rtrim($p->getOutput())); + } + + public function testPreparedCommandWithQuoteInIt() + { + $p = Process::fromShellCommandline('php -r "$code" "$def"'); + $p->run(null, ['code' => 'echo $argv[1];', 'def' => '"DEF"']); + + $this->assertSame('"DEF"', rtrim($p->getOutput())); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException + * @expectedExceptionMessage Command line is missing a value for key "$abc": echo "$abc". + */ + public function testPreparedCommandWithMissingValue() + { + $p = Process::fromShellCommandline('echo "$abc"'); + $p->run(null, ['bcd' => 'BCD']); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException + * @expectedExceptionMessage Command line is missing a value for key "$abc": echo "$abc". + */ + public function testPreparedCommandWithNoValues() + { + $p = Process::fromShellCommandline('echo "$abc"'); + $p->run(null, []); + } + public function testEnvArgument() { $env = ['FOO' => 'Foo', 'BAR' => 'Bar']; diff --git a/src/Symfony/Component/Process/composer.json b/src/Symfony/Component/Process/composer.json index d3efd0238207a..e0174de75533c 100644 --- a/src/Symfony/Component/Process/composer.json +++ b/src/Symfony/Component/Process/composer.json @@ -27,7 +27,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/PropertyAccess/composer.json b/src/Symfony/Component/PropertyAccess/composer.json index bd81e5c260cc2..02eb76a2d6fcd 100644 --- a/src/Symfony/Component/PropertyAccess/composer.json +++ b/src/Symfony/Component/PropertyAccess/composer.json @@ -17,10 +17,10 @@ ], "require": { "php": "^7.1.3", - "symfony/inflector": "~3.4|~4.0" + "symfony/inflector": "^3.4|^4.0|^5.0" }, "require-dev": { - "symfony/cache": "~3.4|~4.0" + "symfony/cache": "^3.4|^4.0|^5.0" }, "suggest": { "psr/cache-implementation": "To cache access methods." @@ -34,7 +34,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/PropertyInfo/composer.json b/src/Symfony/Component/PropertyInfo/composer.json index bfa0fd3eccac1..0184230d3a516 100644 --- a/src/Symfony/Component/PropertyInfo/composer.json +++ b/src/Symfony/Component/PropertyInfo/composer.json @@ -24,12 +24,12 @@ ], "require": { "php": "^7.1.3", - "symfony/inflector": "~3.4|~4.0" + "symfony/inflector": "^3.4|^4.0|^5.0" }, "require-dev": { - "symfony/serializer": "~3.4|~4.0", - "symfony/cache": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", + "symfony/serializer": "^3.4|^4.0|^5.0", + "symfony/cache": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0", "doctrine/annotations": "~1.0" }, @@ -53,7 +53,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Routing/composer.json b/src/Symfony/Component/Routing/composer.json index 77d7ce981c82e..21fe8ee101f71 100644 --- a/src/Symfony/Component/Routing/composer.json +++ b/src/Symfony/Component/Routing/composer.json @@ -19,11 +19,11 @@ "php": "^7.1.3" }, "require-dev": { - "symfony/config": "~4.2", - "symfony/http-foundation": "~3.4|~4.0", - "symfony/yaml": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", + "symfony/config": "^4.2|^5.0", + "symfony/http-foundation": "^3.4|^4.0|^5.0", + "symfony/yaml": "^3.4|^4.0|^5.0", + "symfony/expression-language": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", "doctrine/annotations": "~1.2", "psr/log": "~1.0" }, @@ -48,7 +48,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Security/CHANGELOG.md b/src/Symfony/Component/Security/CHANGELOG.md index 24d15f7e78467..982d753af5091 100644 --- a/src/Symfony/Component/Security/CHANGELOG.md +++ b/src/Symfony/Component/Security/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +4.4.0 +----- + + * Added method `needsRehash()` to `PasswordEncoderInterface` and `UserPasswordEncoderInterface` + * Added `MigratingPasswordEncoder` + 4.3.0 ----- diff --git a/src/Symfony/Component/Security/Core/Encoder/BasePasswordEncoder.php b/src/Symfony/Component/Security/Core/Encoder/BasePasswordEncoder.php index 3a5c4f0c4ba81..ffacede722c94 100644 --- a/src/Symfony/Component/Security/Core/Encoder/BasePasswordEncoder.php +++ b/src/Symfony/Component/Security/Core/Encoder/BasePasswordEncoder.php @@ -20,6 +20,14 @@ abstract class BasePasswordEncoder implements PasswordEncoderInterface { const MAX_PASSWORD_LENGTH = 4096; + /** + * {@inheritdoc} + */ + public function needsRehash(string $encoded): bool + { + return false; + } + /** * Demerges a merge password and salt string. * diff --git a/src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php b/src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php index 150190dc4c161..ad58fd0b7f9cc 100644 --- a/src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php +++ b/src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php @@ -85,7 +85,17 @@ private function createEncoder(array $config) private function getEncoderConfigFromAlgorithm($config) { if ('auto' === $config['algorithm']) { - $config['algorithm'] = SodiumPasswordEncoder::isSupported() ? 'sodium' : 'native'; + $encoderChain = []; + // "plaintext" is not listed as any leaked hashes could then be used to authenticate directly + foreach ([SodiumPasswordEncoder::isSupported() ? 'sodium' : 'native', 'pbkdf2', $config['hash_algorithm']] as $algo) { + $config['algorithm'] = $algo; + $encoderChain[] = $this->createEncoder($config); + } + + return [ + 'class' => MigratingPasswordEncoder::class, + 'arguments' => $encoderChain, + ]; } switch ($config['algorithm']) { diff --git a/src/Symfony/Component/Security/Core/Encoder/MigratingPasswordEncoder.php b/src/Symfony/Component/Security/Core/Encoder/MigratingPasswordEncoder.php new file mode 100644 index 0000000000000..77e6726808f9b --- /dev/null +++ b/src/Symfony/Component/Security/Core/Encoder/MigratingPasswordEncoder.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Encoder; + +/** + * Hashes passwords using the best available encoder. + * Validates them using a chain of encoders. + * + * /!\ Don't put a PlaintextPasswordEncoder in the list as that'd mean a leaked hash + * could be used to authenticate successfully without knowing the cleartext password. + * + * @author Nicolas Grekas + */ +final class MigratingPasswordEncoder extends BasePasswordEncoder implements SelfSaltingEncoderInterface +{ + private $bestEncoder; + private $extraEncoders; + + public function __construct(PasswordEncoderInterface $bestEncoder, PasswordEncoderInterface ...$extraEncoders) + { + $this->bestEncoder = $bestEncoder; + $this->extraEncoders = $extraEncoders; + } + + /** + * {@inheritdoc} + */ + public function encodePassword($raw, $salt) + { + return $this->bestEncoder->encodePassword($raw, $salt); + } + + /** + * {@inheritdoc} + */ + public function isPasswordValid($encoded, $raw, $salt) + { + if ($this->bestEncoder->isPasswordValid($encoded, $raw, $salt)) { + return true; + } + + if (!$this->bestEncoder->needsRehash($encoded)) { + return false; + } + + foreach ($this->extraEncoders as $encoder) { + if ($encoder->isPasswordValid($encoded, $raw, $salt)) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function needsRehash(string $encoded): bool + { + return $this->bestEncoder->needsRehash($encoded); + } +} diff --git a/src/Symfony/Component/Security/Core/Encoder/NativePasswordEncoder.php b/src/Symfony/Component/Security/Core/Encoder/NativePasswordEncoder.php index 94d9f5ca51e6e..84d87c6e241e8 100644 --- a/src/Symfony/Component/Security/Core/Encoder/NativePasswordEncoder.php +++ b/src/Symfony/Component/Security/Core/Encoder/NativePasswordEncoder.php @@ -87,4 +87,12 @@ public function isPasswordValid($encoded, $raw, $salt) return \strlen($raw) <= self::MAX_PASSWORD_LENGTH && password_verify($raw, $encoded); } + + /** + * {@inheritdoc} + */ + public function needsRehash(string $encoded): bool + { + return password_needs_rehash($encoded, $this->algo, $this->options); + } } diff --git a/src/Symfony/Component/Security/Core/Encoder/PasswordEncoderInterface.php b/src/Symfony/Component/Security/Core/Encoder/PasswordEncoderInterface.php index 03cdaca44aef7..38c64a9830daf 100644 --- a/src/Symfony/Component/Security/Core/Encoder/PasswordEncoderInterface.php +++ b/src/Symfony/Component/Security/Core/Encoder/PasswordEncoderInterface.php @@ -17,6 +17,8 @@ * PasswordEncoderInterface is the interface for all encoders. * * @author Fabien Potencier + * + * @method bool needsRehash(string $encoded) */ interface PasswordEncoderInterface { diff --git a/src/Symfony/Component/Security/Core/Encoder/SodiumPasswordEncoder.php b/src/Symfony/Component/Security/Core/Encoder/SodiumPasswordEncoder.php index 01b8b95c8b8f5..66a513f3d26dd 100644 --- a/src/Symfony/Component/Security/Core/Encoder/SodiumPasswordEncoder.php +++ b/src/Symfony/Component/Security/Core/Encoder/SodiumPasswordEncoder.php @@ -95,4 +95,20 @@ public function isPasswordValid($encoded, $raw, $salt) throw new LogicException('Libsodium is not available. You should either install the sodium extension, upgrade to PHP 7.2+ or use a different encoder.'); } + + /** + * {@inheritdoc} + */ + public function needsRehash(string $encoded): bool + { + if (\function_exists('sodium_crypto_pwhash_str_needs_rehash')) { + return sodium_crypto_pwhash_str_needs_rehash($encoded, $this->opsLimit, $this->memLimit); + } + + if (\extension_loaded('libsodium')) { + return \Sodium\crypto_pwhash_str_needs_rehash($encoded, $this->opsLimit, $this->memLimit); + } + + throw new LogicException('Libsodium is not available. You should either install the sodium extension, upgrade to PHP 7.2+ or use a different encoder.'); + } } diff --git a/src/Symfony/Component/Security/Core/Encoder/UserPasswordEncoder.php b/src/Symfony/Component/Security/Core/Encoder/UserPasswordEncoder.php index 3efc8c6d48bb5..ad9d929deb4cd 100644 --- a/src/Symfony/Component/Security/Core/Encoder/UserPasswordEncoder.php +++ b/src/Symfony/Component/Security/Core/Encoder/UserPasswordEncoder.php @@ -46,4 +46,14 @@ public function isPasswordValid(UserInterface $user, $raw) return $encoder->isPasswordValid($user->getPassword(), $raw, $user->getSalt()); } + + /** + * {@inheritdoc} + */ + public function needsRehash(UserInterface $user, string $encoded): bool + { + $encoder = $this->encoderFactory->getEncoder($user); + + return method_exists($encoder, 'needsRehash') && $encoder->needsRehash($encoded); + } } diff --git a/src/Symfony/Component/Security/Core/Encoder/UserPasswordEncoderInterface.php b/src/Symfony/Component/Security/Core/Encoder/UserPasswordEncoderInterface.php index 7861caab20ca6..911bbe5282d9d 100644 --- a/src/Symfony/Component/Security/Core/Encoder/UserPasswordEncoderInterface.php +++ b/src/Symfony/Component/Security/Core/Encoder/UserPasswordEncoderInterface.php @@ -17,6 +17,8 @@ * UserPasswordEncoderInterface is the interface for the password encoder service. * * @author Ariel Ferrandini + * + * @method bool needsRehash(UserInterface $user, string $encoded) */ interface UserPasswordEncoderInterface { diff --git a/src/Symfony/Component/Security/Core/Tests/Encoder/BasePasswordEncoderTest.php b/src/Symfony/Component/Security/Core/Tests/Encoder/BasePasswordEncoderTest.php index 2251cfdf906e0..2b101c3b3b606 100644 --- a/src/Symfony/Component/Security/Core/Tests/Encoder/BasePasswordEncoderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Encoder/BasePasswordEncoderTest.php @@ -60,6 +60,12 @@ public function testIsPasswordTooLong() $this->assertFalse($this->invokeIsPasswordTooLong(str_repeat('a', 10))); } + public function testNeedsRehash() + { + $encoder = new PasswordEncoder(); + $this->assertFalse($encoder->needsRehash('foo')); + } + protected function invokeDemergePasswordAndSalt($password) { $encoder = new PasswordEncoder(); diff --git a/src/Symfony/Component/Security/Core/Tests/Encoder/MigratingPasswordEncoderTest.php b/src/Symfony/Component/Security/Core/Tests/Encoder/MigratingPasswordEncoderTest.php new file mode 100644 index 0000000000000..245d6c182d0fa --- /dev/null +++ b/src/Symfony/Component/Security/Core/Tests/Encoder/MigratingPasswordEncoderTest.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Tests\Encoder; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Security\Core\Encoder\MigratingPasswordEncoder; +use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder; +use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface; + +class MigratingPasswordEncoderTest extends TestCase +{ + public function testValidation() + { + $bestEncoder = new NativePasswordEncoder(4, 12000, 4); + + $extraEncoder = $this->getMockBuilder(TestPasswordEncoderInterface::class)->getMock(); + $extraEncoder->expects($this->never())->method('encodePassword'); + $extraEncoder->expects($this->never())->method('isPasswordValid'); + $extraEncoder->expects($this->never())->method('needsRehash'); + + $encoder = new MigratingPasswordEncoder($bestEncoder, $extraEncoder); + + $this->assertTrue($encoder->needsRehash('foo')); + + $hash = $encoder->encodePassword('foo', 'salt'); + $this->assertFalse($encoder->needsRehash($hash)); + + $this->assertTrue($encoder->isPasswordValid($hash, 'foo', 'salt')); + $this->assertFalse($encoder->isPasswordValid($hash, 'bar', 'salt')); + } + + public function testFallback() + { + $bestEncoder = new NativePasswordEncoder(4, 12000, 4); + + $extraEncoder1 = $this->getMockBuilder(TestPasswordEncoderInterface::class)->getMock(); + $extraEncoder1->expects($this->any()) + ->method('isPasswordValid') + ->with('abc', 'foo', 'salt') + ->willReturn(true); + + $encoder = new MigratingPasswordEncoder($bestEncoder, $extraEncoder1); + + $this->assertTrue($encoder->isPasswordValid('abc', 'foo', 'salt')); + + $extraEncoder2 = $this->getMockBuilder(TestPasswordEncoderInterface::class)->getMock(); + $extraEncoder2->expects($this->any()) + ->method('isPasswordValid') + ->willReturn(false); + + $encoder = new MigratingPasswordEncoder($bestEncoder, $extraEncoder2); + + $this->assertFalse($encoder->isPasswordValid('abc', 'foo', 'salt')); + + $encoder = new MigratingPasswordEncoder($bestEncoder, $extraEncoder2, $extraEncoder1); + + $this->assertTrue($encoder->isPasswordValid('abc', 'foo', 'salt')); + } +} + +interface TestPasswordEncoderInterface extends PasswordEncoderInterface +{ + public function needsRehash(string $encoded): bool; +} diff --git a/src/Symfony/Component/Security/Core/Tests/Encoder/NativePasswordEncoderTest.php b/src/Symfony/Component/Security/Core/Tests/Encoder/NativePasswordEncoderTest.php index 681b91a1eeec5..55e518b49100c 100644 --- a/src/Symfony/Component/Security/Core/Tests/Encoder/NativePasswordEncoderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Encoder/NativePasswordEncoderTest.php @@ -67,4 +67,17 @@ public function testCheckPasswordLength() $this->assertFalse($encoder->isPasswordValid($result, str_repeat('a', 73), 'salt')); $this->assertTrue($encoder->isPasswordValid($result, str_repeat('a', 72), 'salt')); } + + public function testNeedsRehash() + { + $encoder = new NativePasswordEncoder(4, 11000, 4); + + $this->assertTrue($encoder->needsRehash('dummyhash')); + + $hash = $encoder->encodePassword('foo', 'salt'); + $this->assertFalse($encoder->needsRehash($hash)); + + $encoder = new NativePasswordEncoder(5, 11000, 5); + $this->assertTrue($encoder->needsRehash($hash)); + } } diff --git a/src/Symfony/Component/Security/Core/Tests/Encoder/SodiumPasswordEncoderTest.php b/src/Symfony/Component/Security/Core/Tests/Encoder/SodiumPasswordEncoderTest.php index 84c8b4849e2b5..daa0da0120cb2 100644 --- a/src/Symfony/Component/Security/Core/Tests/Encoder/SodiumPasswordEncoderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Encoder/SodiumPasswordEncoderTest.php @@ -60,4 +60,17 @@ public function testUserProvidedSaltIsNotUsed() $result = $encoder->encodePassword('password', 'salt'); $this->assertTrue($encoder->isPasswordValid($result, 'password', 'anotherSalt')); } + + public function testNeedsRehash() + { + $encoder = new SodiumPasswordEncoder(4, 11000); + + $this->assertTrue($encoder->needsRehash('dummyhash')); + + $hash = $encoder->encodePassword('foo', 'salt'); + $this->assertFalse($encoder->needsRehash($hash)); + + $encoder = new SodiumPasswordEncoder(5, 11000); + $this->assertTrue($encoder->needsRehash($hash)); + } } diff --git a/src/Symfony/Component/Security/Core/Tests/Encoder/UserPasswordEncoderTest.php b/src/Symfony/Component/Security/Core/Tests/Encoder/UserPasswordEncoderTest.php index 41a602f976047..9bd10a964282c 100644 --- a/src/Symfony/Component/Security/Core/Tests/Encoder/UserPasswordEncoderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Encoder/UserPasswordEncoderTest.php @@ -12,7 +12,10 @@ namespace Symfony\Component\Security\Core\Tests\Encoder; use PHPUnit\Framework\TestCase; +use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; +use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder; use Symfony\Component\Security\Core\Encoder\UserPasswordEncoder; +use Symfony\Component\Security\Core\User\User; class UserPasswordEncoderTest extends TestCase { @@ -68,4 +71,23 @@ public function testIsPasswordValid() $isValid = $passwordEncoder->isPasswordValid($userMock, 'plainPassword'); $this->assertTrue($isValid); } + + public function testNeedsRehash() + { + $user = new User('username', null); + $encoder = new NativePasswordEncoder(4, 20000, 4); + + $mockEncoderFactory = $this->getMockBuilder(EncoderFactoryInterface::class)->getMock(); + $mockEncoderFactory->expects($this->any()) + ->method('getEncoder') + ->with($user) + ->will($this->onConsecutiveCalls($encoder, $encoder, new NativePasswordEncoder(5, 20000, 5), $encoder)); + + $passwordEncoder = new UserPasswordEncoder($mockEncoderFactory); + + $hash = $passwordEncoder->encodePassword($user, 'foo', 'salt'); + $this->assertFalse($passwordEncoder->needsRehash($user, $hash)); + $this->assertTrue($passwordEncoder->needsRehash($user, $hash)); + $this->assertFalse($passwordEncoder->needsRehash($user, $hash)); + } } diff --git a/src/Symfony/Component/Security/Core/Tests/User/LdapUserProviderTest.php b/src/Symfony/Component/Security/Core/Tests/User/LdapUserProviderTest.php index 39a346433e463..bca36c7b7f843 100644 --- a/src/Symfony/Component/Security/Core/Tests/User/LdapUserProviderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/User/LdapUserProviderTest.php @@ -334,6 +334,7 @@ public function testLoadUserByUsernameIsSuccessfulWithPasswordAttribute() ->willReturn(new Entry('foo', [ 'sAMAccountName' => ['foo'], 'userpassword' => ['bar'], + 'email' => ['elsa@symfony.com'], ] )) ; @@ -353,7 +354,7 @@ public function testLoadUserByUsernameIsSuccessfulWithPasswordAttribute() ->willReturn($query) ; - $provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, [], 'sAMAccountName', '({uid_key}={username})', 'userpassword'); + $provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, [], 'sAMAccountName', '({uid_key}={username})', 'userpassword', ['email']); $this->assertInstanceOf( 'Symfony\Component\Security\Core\User\User', $provider->loadUserByUsername('foo') diff --git a/src/Symfony/Component/Security/Core/User/LdapUserProvider.php b/src/Symfony/Component/Security/Core/User/LdapUserProvider.php index adb820fccaf35..e467b3c3e0407 100644 --- a/src/Symfony/Component/Security/Core/User/LdapUserProvider.php +++ b/src/Symfony/Component/Security/Core/User/LdapUserProvider.php @@ -34,8 +34,9 @@ class LdapUserProvider implements UserProviderInterface private $uidKey; private $defaultSearch; private $passwordAttribute; + private $extraFields; - public function __construct(LdapInterface $ldap, string $baseDn, string $searchDn = null, string $searchPassword = null, array $defaultRoles = [], string $uidKey = null, string $filter = null, string $passwordAttribute = null) + public function __construct(LdapInterface $ldap, string $baseDn, string $searchDn = null, string $searchPassword = null, array $defaultRoles = [], string $uidKey = null, string $filter = null, string $passwordAttribute = null, array $extraFields = []) { if (null === $uidKey) { $uidKey = 'sAMAccountName'; @@ -53,6 +54,7 @@ public function __construct(LdapInterface $ldap, string $baseDn, string $searchD $this->uidKey = $uidKey; $this->defaultSearch = str_replace('{uid_key}', $uidKey, $filter); $this->passwordAttribute = $passwordAttribute; + $this->extraFields = $extraFields; } /** @@ -123,12 +125,17 @@ public function supportsClass($class) protected function loadUser($username, Entry $entry) { $password = null; + $extraFields = []; if (null !== $this->passwordAttribute) { $password = $this->getAttributeValue($entry, $this->passwordAttribute); } - return new User($username, $password, $this->defaultRoles); + foreach ($this->extraFields as $field) { + $extraFields[$field] = $this->getAttributeValue($entry, $field); + } + + return new User($username, $password, $this->defaultRoles, true, true, true, true, $extraFields); } /** diff --git a/src/Symfony/Component/Security/Core/User/User.php b/src/Symfony/Component/Security/Core/User/User.php index 18faeb7af0402..19f9c3e8c74a7 100644 --- a/src/Symfony/Component/Security/Core/User/User.php +++ b/src/Symfony/Component/Security/Core/User/User.php @@ -27,8 +27,9 @@ final class User implements UserInterface, EquatableInterface, AdvancedUserInter private $credentialsNonExpired; private $accountNonLocked; private $roles; + private $extraFields; - public function __construct(?string $username, ?string $password, array $roles = [], bool $enabled = true, bool $userNonExpired = true, bool $credentialsNonExpired = true, bool $userNonLocked = true) + public function __construct(?string $username, ?string $password, array $roles = [], bool $enabled = true, bool $userNonExpired = true, bool $credentialsNonExpired = true, bool $userNonLocked = true, array $extraFields = []) { if ('' === $username || null === $username) { throw new \InvalidArgumentException('The username cannot be empty.'); @@ -41,6 +42,7 @@ public function __construct(?string $username, ?string $password, array $roles = $this->credentialsNonExpired = $credentialsNonExpired; $this->accountNonLocked = $userNonLocked; $this->roles = $roles; + $this->extraFields = $extraFields; } public function __toString() @@ -118,6 +120,11 @@ public function eraseCredentials() { } + public function getExtraFields(): array + { + return $this->extraFields; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Security/Core/composer.json b/src/Symfony/Component/Security/Core/composer.json index f2bbd449677bb..73f8078ab5ea6 100644 --- a/src/Symfony/Component/Security/Core/composer.json +++ b/src/Symfony/Component/Security/Core/composer.json @@ -23,14 +23,14 @@ "require-dev": { "psr/container": "^1.0", "symfony/event-dispatcher": "^4.3", - "symfony/expression-language": "~3.4|~4.0", - "symfony/http-foundation": "~3.4|~4.0", - "symfony/ldap": "~3.4|~4.0", - "symfony/validator": "~3.4|~4.0", + "symfony/expression-language": "^3.4|^4.0|^5.0", + "symfony/http-foundation": "^3.4|^4.0|^5.0", + "symfony/ldap": "^3.4|^4.0|^5.0", + "symfony/validator": "^3.4|^4.0|^5.0", "psr/log": "~1.0" }, "conflict": { - "symfony/event-dispatcher": "<4.3", + "symfony/event-dispatcher": "<4.3|>=5", "symfony/security-guard": "<4.3" }, "suggest": { @@ -50,7 +50,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Security/Csrf/composer.json b/src/Symfony/Component/Security/Csrf/composer.json index 716951f95bdf0..81359829f8665 100644 --- a/src/Symfony/Component/Security/Csrf/composer.json +++ b/src/Symfony/Component/Security/Csrf/composer.json @@ -17,10 +17,10 @@ ], "require": { "php": "^7.1.3", - "symfony/security-core": "~3.4|~4.0" + "symfony/security-core": "^3.4|^4.0|^5.0" }, "require-dev": { - "symfony/http-foundation": "~3.4|~4.0" + "symfony/http-foundation": "^3.4|^4.0|^5.0" }, "conflict": { "symfony/http-foundation": "<3.4" @@ -37,7 +37,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Security/Guard/composer.json b/src/Symfony/Component/Security/Guard/composer.json index f424f4a295662..af3ce94a9b2d0 100644 --- a/src/Symfony/Component/Security/Guard/composer.json +++ b/src/Symfony/Component/Security/Guard/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": "^7.1.3", - "symfony/security-core": "~3.4.22|^4.2.3", + "symfony/security-core": "^3.4.22|^4.2.3|^5.0", "symfony/security-http": "^4.3" }, "require-dev": { @@ -32,7 +32,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php b/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php index 76a5a9107b4c2..a89874a0808f2 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php @@ -208,7 +208,7 @@ private function startAuthentication(Request $request, AuthenticationException $ protected function setTargetPath(Request $request) { // session isn't required when using HTTP basic authentication mechanism for example - if ($request->hasSession() && $request->isMethodSafe(false) && !$request->isXmlHttpRequest()) { + if ($request->hasSession() && $request->isMethodSafe() && !$request->isXmlHttpRequest()) { $this->saveTargetPath($request->getSession(), $this->providerKey, $request->getUri()); } } diff --git a/src/Symfony/Component/Security/Http/composer.json b/src/Symfony/Component/Security/Http/composer.json index 402951b308403..1da2cab878f8c 100644 --- a/src/Symfony/Component/Security/Http/composer.json +++ b/src/Symfony/Component/Security/Http/composer.json @@ -18,16 +18,17 @@ "require": { "php": "^7.1.3", "symfony/security-core": "^4.3", - "symfony/http-foundation": "~3.4|~4.0", + "symfony/http-foundation": "^3.4|^4.0|^5.0", "symfony/http-kernel": "^4.3", - "symfony/property-access": "~3.4|~4.0" + "symfony/property-access": "^3.4|^4.0|^5.0" }, "require-dev": { - "symfony/routing": "~3.4|~4.0", - "symfony/security-csrf": "^3.4.11|^4.0.11", + "symfony/routing": "^3.4|^4.0|^5.0", + "symfony/security-csrf": "^3.4.11|^4.0.11|^5.0", "psr/log": "~1.0" }, "conflict": { + "symfony/event-dispatcher": ">=5", "symfony/security-csrf": "<3.4.11|~4.0,<4.0.11" }, "suggest": { @@ -43,7 +44,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Security/composer.json b/src/Symfony/Component/Security/composer.json index 6365520893de4..20b29bb106a10 100644 --- a/src/Symfony/Component/Security/composer.json +++ b/src/Symfony/Component/Security/composer.json @@ -18,9 +18,9 @@ "require": { "php": "^7.1.3", "symfony/event-dispatcher-contracts": "^1.1", - "symfony/http-foundation": "~3.4|~4.0", + "symfony/http-foundation": "^3.4|^4.0|^5.0", "symfony/http-kernel": "^4.3", - "symfony/property-access": "~3.4|~4.0", + "symfony/property-access": "^3.4|^4.0|^5.0", "symfony/service-contracts": "^1.1" }, "replace": { @@ -31,15 +31,18 @@ }, "require-dev": { "psr/container": "^1.0", - "symfony/finder": "~3.4|~4.0", + "symfony/finder": "^3.4|^4.0|^5.0", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-icu": "~1.0", - "symfony/routing": "~3.4|~4.0", - "symfony/validator": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/ldap": "~3.4|~4.0", + "symfony/routing": "^3.4|^4.0|^5.0", + "symfony/validator": "^3.4|^4.0|^5.0", + "symfony/expression-language": "^3.4|^4.0|^5.0", + "symfony/ldap": "^3.4|^4.0|^5.0", "psr/log": "~1.0" }, + "conflict": { + "symfony/event-dispatcher": ">=5" + }, "suggest": { "psr/container-implementation": "To instantiate the Security class", "symfony/form": "", @@ -60,7 +63,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Serializer/composer.json b/src/Symfony/Component/Serializer/composer.json index 087289fd7ffc1..45c185a364e7e 100644 --- a/src/Symfony/Component/Serializer/composer.json +++ b/src/Symfony/Component/Serializer/composer.json @@ -20,15 +20,15 @@ "symfony/polyfill-ctype": "~1.8" }, "require-dev": { - "symfony/yaml": "~3.4|~4.0", - "symfony/config": "~3.4|~4.0", - "symfony/property-access": "~3.4|~4.0", - "symfony/http-foundation": "~3.4|~4.0", - "symfony/cache": "~3.4|~4.0", - "symfony/property-info": "^3.4.13|~4.0", - "symfony/validator": "~3.4|~4.0", + "symfony/yaml": "^3.4|^4.0|^5.0", + "symfony/config": "^3.4|^4.0|^5.0", + "symfony/property-access": "^3.4|^4.0|^5.0", + "symfony/http-foundation": "^3.4|^4.0|^5.0", + "symfony/cache": "^3.4|^4.0|^5.0", + "symfony/property-info": "^3.4.13|~4.0|^5.0", + "symfony/validator": "^3.4|^4.0|^5.0", "doctrine/annotations": "~1.0", - "symfony/dependency-injection": "~3.4|~4.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", "doctrine/cache": "~1.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0" }, @@ -58,7 +58,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Stopwatch/composer.json b/src/Symfony/Component/Stopwatch/composer.json index 68c4d9f2e0db3..68cc9dab75901 100644 --- a/src/Symfony/Component/Stopwatch/composer.json +++ b/src/Symfony/Component/Stopwatch/composer.json @@ -28,7 +28,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Templating/composer.json b/src/Symfony/Component/Templating/composer.json index f6474495d5687..f785cf1cca666 100644 --- a/src/Symfony/Component/Templating/composer.json +++ b/src/Symfony/Component/Templating/composer.json @@ -34,7 +34,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Translation/Loader/ArrayLoader.php b/src/Symfony/Component/Translation/Loader/ArrayLoader.php index 0a6f9f089d5b7..2e9a4285ec97f 100644 --- a/src/Symfony/Component/Translation/Loader/ArrayLoader.php +++ b/src/Symfony/Component/Translation/Loader/ArrayLoader.php @@ -25,7 +25,7 @@ class ArrayLoader implements LoaderInterface */ public function load($resource, $locale, $domain = 'messages') { - $this->flatten($resource); + $resource = $this->flatten($resource); $catalogue = new MessageCatalogue($locale); $catalogue->add($resource, $domain); @@ -39,28 +39,20 @@ public function load($resource, $locale, $domain = 'messages') * 'key' => ['key2' => ['key3' => 'value']] * Becomes: * 'key.key2.key3' => 'value' - * - * This function takes an array by reference and will modify it - * - * @param array &$messages The array that will be flattened - * @param array $subnode Current subnode being parsed, used internally for recursive calls - * @param string $path Current path being parsed, used internally for recursive calls */ - private function flatten(array &$messages, array $subnode = null, $path = null) + private function flatten(array $messages): array { - if (null === $subnode) { - $subnode = &$messages; - } - foreach ($subnode as $key => $value) { + $result = []; + foreach ($messages as $key => $value) { if (\is_array($value)) { - $nodePath = $path ? $path.'.'.$key : $key; - $this->flatten($messages, $value, $nodePath); - if (null === $path) { - unset($messages[$key]); + foreach ($this->flatten($value) as $k => $v) { + $result[$key.'.'.$k] = $v; } - } elseif (null !== $path) { - $messages[$path.'.'.$key] = $value; + } else { + $result[$key] = $value; } } + + return $result; } } diff --git a/src/Symfony/Component/Translation/composer.json b/src/Symfony/Component/Translation/composer.json index fd25a4cf0448e..f390623efe0b2 100644 --- a/src/Symfony/Component/Translation/composer.json +++ b/src/Symfony/Component/Translation/composer.json @@ -21,15 +21,15 @@ "symfony/translation-contracts": "^1.1.2" }, "require-dev": { - "symfony/config": "~3.4|~4.0", - "symfony/console": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/http-kernel": "~3.4|~4.0", - "symfony/intl": "~3.4|~4.0", + "symfony/config": "^3.4|^4.0|^5.0", + "symfony/console": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/http-kernel": "^3.4|^4.0|^5.0", + "symfony/intl": "^3.4|^4.0|^5.0", "symfony/service-contracts": "^1.1.2", - "symfony/var-dumper": "~3.4|~4.0", - "symfony/yaml": "~3.4|~4.0", - "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/var-dumper": "^3.4|^4.0|^5.0", + "symfony/yaml": "^3.4|^4.0|^5.0", + "symfony/finder": "~2.8|~3.0|~4.0|^5.0", "psr/log": "~1.0" }, "conflict": { @@ -54,7 +54,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Validator/CHANGELOG.md b/src/Symfony/Component/Validator/CHANGELOG.md index eabec446b768c..8a85ee35efcfa 100644 --- a/src/Symfony/Component/Validator/CHANGELOG.md +++ b/src/Symfony/Component/Validator/CHANGELOG.md @@ -1,6 +1,14 @@ CHANGELOG ========= +4.4.0 +----- + + * deprecated passing an `ExpressionLanguage` instance as the second argument of `ExpressionValidator::__construct()`. Pass it as the first argument instead. + * added the `compared_value_path` parameter in violations when using any + comparison constraint with the `propertyPath` option. + * added support for checking an array of types in `TypeValidator` + 4.3.0 ----- diff --git a/src/Symfony/Component/Validator/ConstraintViolation.php b/src/Symfony/Component/Validator/ConstraintViolation.php index 8651913c3e7f3..2ec15e4309c54 100644 --- a/src/Symfony/Component/Validator/ConstraintViolation.php +++ b/src/Symfony/Component/Validator/ConstraintViolation.php @@ -44,13 +44,18 @@ class ConstraintViolation implements ConstraintViolationInterface * violation * @param int|null $plural The number for determining the plural * form when translating the message - * @param mixed $code The error code of the violation + * @param string|null $code The error code of the violation * @param Constraint|null $constraint The constraint whose validation * caused the violation * @param mixed $cause The cause of the violation */ public function __construct(?string $message, ?string $messageTemplate, array $parameters, $root, ?string $propertyPath, $invalidValue, int $plural = null, $code = null, Constraint $constraint = null, $cause = null) { + if (null === $message) { + @trigger_error(sprintf('Passing a null message when instantiating a "%s" is deprecated since Symfony 4.4.', __CLASS__), E_USER_DEPRECATED); + $message = ''; + } + $this->message = $message; $this->messageTemplate = $messageTemplate; $this->parameters = $parameters; diff --git a/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php b/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php index 3c95c097e8e9a..00ddbb36532aa 100644 --- a/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php +++ b/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php @@ -77,12 +77,17 @@ public function validate($value, Constraint $constraint) } if (!$this->compareValues($value, $comparedValue)) { - $this->context->buildViolation($constraint->message) + $violationBuilder = $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value, self::OBJECT_TO_STRING | self::PRETTY_DATE)) ->setParameter('{{ compared_value }}', $this->formatValue($comparedValue, self::OBJECT_TO_STRING | self::PRETTY_DATE)) ->setParameter('{{ compared_value_type }}', $this->formatTypeOf($comparedValue)) - ->setCode($this->getErrorCode()) - ->addViolation(); + ->setCode($this->getErrorCode()); + + if (null !== $path) { + $violationBuilder->setParameter('{{ compared_value_path }}', $path); + } + + $violationBuilder->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php b/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php index b72a83365b7b7..58ce1606a9c79 100644 --- a/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php +++ b/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php @@ -25,8 +25,20 @@ class ExpressionValidator extends ConstraintValidator { private $expressionLanguage; - public function __construct($propertyAccessor = null, ExpressionLanguage $expressionLanguage = null) + public function __construct(/*ExpressionLanguage */$expressionLanguage = null) { + if (\func_num_args() > 1) { + @trigger_error(sprintf('The "%s" instance should be passed as "%s" first argument instead of second argument since 4.4.', ExpressionLanguage::class, __METHOD__), E_USER_DEPRECATED); + + $expressionLanguage = func_get_arg(1); + + if (null !== $expressionLanguage && !$expressionLanguage instanceof ExpressionLanguage) { + throw new \TypeError(sprintf('Argument 2 passed to %s() must be an instance of %s or null, %s given. Since 4.4, passing it as the second argument is deprecated and will trigger a deprecation. Pass it as the first argument instead.', __METHOD__, ExpressionLanguage::class, \is_object($expressionLanguage) ? \get_class($expressionLanguage) : \gettype($expressionLanguage))); + } + } elseif (null !== $expressionLanguage && !$expressionLanguage instanceof ExpressionLanguage) { + @trigger_error(sprintf('The "%s" first argument must be an instance of "%s" or null since 4.4. "%s" given', __METHOD__, ExpressionLanguage::class, \is_object($expressionLanguage) ? \get_class($expressionLanguage) : \gettype($expressionLanguage)), E_USER_DEPRECATED); + } + $this->expressionLanguage = $expressionLanguage; } diff --git a/src/Symfony/Component/Validator/Constraints/TypeValidator.php b/src/Symfony/Component/Validator/Constraints/TypeValidator.php index 206836d3617fc..ebcf50165e14d 100644 --- a/src/Symfony/Component/Validator/Constraints/TypeValidator.php +++ b/src/Symfony/Component/Validator/Constraints/TypeValidator.php @@ -33,22 +33,25 @@ public function validate($value, Constraint $constraint) return; } - $type = strtolower($constraint->type); - $type = 'boolean' == $type ? 'bool' : $constraint->type; - $isFunction = 'is_'.$type; - $ctypeFunction = 'ctype_'.$type; - - if (\function_exists($isFunction) && $isFunction($value)) { - return; - } elseif (\function_exists($ctypeFunction) && $ctypeFunction($value)) { - return; - } elseif ($value instanceof $constraint->type) { - return; + $types = (array) $constraint->type; + + foreach ($types as $type) { + $type = strtolower($type); + $type = 'boolean' === $type ? 'bool' : $type; + $isFunction = 'is_'.$type; + $ctypeFunction = 'ctype_'.$type; + if (\function_exists($isFunction) && $isFunction($value)) { + return; + } elseif (\function_exists($ctypeFunction) && $ctypeFunction($value)) { + return; + } elseif ($value instanceof $type) { + return; + } } $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) - ->setParameter('{{ type }}', $constraint->type) + ->setParameter('{{ type }}', implode('|', $types)) ->setCode(Type::INVALID_TYPE_ERROR) ->addViolation(); } diff --git a/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php b/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php index 35e78268d447c..b4ec2037fc497 100644 --- a/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php +++ b/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php @@ -99,6 +99,7 @@ protected function restoreDefaultTimezone() protected function createContext() { $translator = $this->getMockBuilder(TranslatorInterface::class)->getMock(); + $translator->expects($this->any())->method('trans')->willReturnArgument(0); $validator = $this->getMockBuilder('Symfony\Component\Validator\Validator\ValidatorInterface')->getMock(); $contextualValidator = $this->getMockBuilder('Symfony\Component\Validator\Validator\ContextualValidatorInterface')->getMock(); @@ -330,7 +331,7 @@ public function assertRaised() private function getViolation() { return new ConstraintViolation( - null, + $this->message, $this->message, $this->parameters, $this->context->getRoot(), diff --git a/src/Symfony/Component/Validator/Tests/Constraints/AbstractComparisonValidatorTestCase.php b/src/Symfony/Component/Validator/Tests/Constraints/AbstractComparisonValidatorTestCase.php index 2f27974a801ab..5979e3942ccc2 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/AbstractComparisonValidatorTestCase.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/AbstractComparisonValidatorTestCase.php @@ -231,6 +231,28 @@ public function testInvalidComparisonToValue($dirtyValue, $dirtyValueAsString, $ ->assertRaised(); } + public function testInvalidComparisonToPropertyPathAddsPathAsParameter() + { + list($dirtyValue, $dirtyValueAsString, $comparedValue, $comparedValueString, $comparedValueType) = current($this->provideAllInvalidComparisons()); + + $constraint = $this->createConstraint(['propertyPath' => 'value']); + $constraint->message = 'Constraint Message'; + + $object = new ComparisonTest_Class($comparedValue); + + $this->setObject($object); + + $this->validator->validate($dirtyValue, $constraint); + + $this->buildViolation('Constraint Message') + ->setParameter('{{ value }}', $dirtyValueAsString) + ->setParameter('{{ compared_value }}', $comparedValueString) + ->setParameter('{{ compared_value_path }}', 'value') + ->setParameter('{{ compared_value_type }}', $comparedValueType) + ->setCode($this->getErrorCode()) + ->assertRaised(); + } + /** * @return array */ diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php index 7e1b460a807a0..e1507fa8e93fe 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Symfony\Component\Validator\Constraints\Expression; use Symfony\Component\Validator\Constraints\ExpressionValidator; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; @@ -253,6 +254,34 @@ public function testExpressionLanguageUsage() 'expression' => 'false', ]); + $expressionLanguage = $this->getMockBuilder(ExpressionLanguage::class)->getMock(); + + $used = false; + + $expressionLanguage->method('evaluate') + ->willReturnCallback(function () use (&$used) { + $used = true; + + return true; + }); + + $validator = new ExpressionValidator($expressionLanguage); + $validator->initialize($this->createContext()); + $validator->validate(null, $constraint); + + $this->assertTrue($used, 'Failed asserting that custom ExpressionLanguage instance is used.'); + } + + /** + * @group legacy + * @expectedDeprecation The "Symfony\Component\ExpressionLanguage\ExpressionLanguage" instance should be passed as "Symfony\Component\Validator\Constraints\ExpressionValidator::__construct" first argument instead of second argument since 4.4. + */ + public function testLegacyExpressionLanguageUsage() + { + $constraint = new Expression([ + 'expression' => 'false', + ]); + $expressionLanguage = $this->getMockBuilder('Symfony\Component\ExpressionLanguage\ExpressionLanguage')->getMock(); $used = false; @@ -271,6 +300,15 @@ public function testExpressionLanguageUsage() $this->assertTrue($used, 'Failed asserting that custom ExpressionLanguage instance is used.'); } + /** + * @group legacy + * @expectedDeprecation The "Symfony\Component\Validator\Constraints\ExpressionValidator::__construct" first argument must be an instance of "Symfony\Component\ExpressionLanguage\ExpressionLanguage" or null since 4.4. "string" given + */ + public function testConstructorInvalidType() + { + new ExpressionValidator('foo'); + } + public function testPassingCustomValues() { $constraint = new Expression([ diff --git a/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php b/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php index 7ac3e57919ae6..6a81129ef9bc5 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php @@ -108,4 +108,9 @@ public function testValidComparisonToPropertyPathOnArray($comparedValue) { $this->markTestSkipped('PropertyPath option is not used in Positive constraint'); } + + public function testInvalidComparisonToPropertyPathAddsPathAsParameter() + { + $this->markTestSkipped('PropertyPath option is not used in PositiveOrZero constraint'); + } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanValidatorWithPositiveConstraintTest.php b/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanValidatorWithPositiveConstraintTest.php index 7a33e1553058c..ca75286dc754c 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanValidatorWithPositiveConstraintTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanValidatorWithPositiveConstraintTest.php @@ -111,4 +111,9 @@ public function testValidComparisonToPropertyPathOnArray($comparedValue) { $this->markTestSkipped('PropertyPath option is not used in Positive constraint'); } + + public function testInvalidComparisonToPropertyPathAddsPathAsParameter() + { + $this->markTestSkipped('PropertyPath option is not used in Positive constraint'); + } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php b/src/Symfony/Component/Validator/Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php index fa7fa2ec23461..45ebad541bafb 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php @@ -111,4 +111,9 @@ public function testValidComparisonToPropertyPathOnArray($comparedValue) { $this->markTestSkipped('PropertyPath option is not used in NegativeOrZero constraint'); } + + public function testInvalidComparisonToPropertyPathAddsPathAsParameter() + { + $this->markTestSkipped('PropertyPath option is not used in NegativeOrZero constraint'); + } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php b/src/Symfony/Component/Validator/Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php index d3e2b7afb38ad..1a8fff7989e67 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php @@ -111,4 +111,9 @@ public function testValidComparisonToPropertyPathOnArray($comparedValue) { $this->markTestSkipped('PropertyPath option is not used in Positive constraint'); } + + public function testInvalidComparisonToPropertyPathAddsPathAsParameter() + { + $this->markTestSkipped('PropertyPath option is not used in Negative constraint'); + } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/TypeValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/TypeValidatorTest.php index 17334bea7925a..af032ef761d16 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/TypeValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/TypeValidatorTest.php @@ -163,6 +163,52 @@ public function getInvalidValues() ]; } + /** + * @dataProvider getValidValuesMultipleTypes + */ + public function testValidValuesMultipleTypes($value, array $types) + { + $constraint = new Type(['type' => $types]); + + $this->validator->validate($value, $constraint); + + $this->assertNoViolation(); + } + + public function getValidValuesMultipleTypes() + { + return [ + ['12345', ['array', 'string']], + [[], ['array', 'string']], + ]; + } + + /** + * @dataProvider getInvalidValuesMultipleTypes + */ + public function testInvalidValuesMultipleTypes($value, $types, $valueAsString) + { + $constraint = new Type([ + 'type' => $types, + 'message' => 'myMessage', + ]); + + $this->validator->validate($value, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', $valueAsString) + ->setParameter('{{ type }}', implode('|', $types)) + ->setCode(Type::INVALID_TYPE_ERROR) + ->assertRaised(); + } + + public function getInvalidValuesMultipleTypes() + { + return [ + ['12345', ['boolean', 'array'], '"12345"'], + ]; + } + protected function createFile() { if (!static::$file) { diff --git a/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilder.php b/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilder.php index c5b1d0b83f013..ed18f6fa8e542 100644 --- a/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilder.php +++ b/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilder.php @@ -47,8 +47,12 @@ class ConstraintViolationBuilder implements ConstraintViolationBuilderInterface /** * @param TranslatorInterface $translator */ - public function __construct(ConstraintViolationList $violations, Constraint $constraint, $message, array $parameters, $root, $propertyPath, $invalidValue, $translator, $translationDomain = null) + public function __construct(ConstraintViolationList $violations, Constraint $constraint, ?string $message, array $parameters, $root, string $propertyPath, $invalidValue, $translator, string $translationDomain = null) { + if (null === $message) { + @trigger_error(sprintf('Passing a null message when instantiating a "%s" is deprecated since Symfony 4.4.', __CLASS__), E_USER_DEPRECATED); + $message = ''; + } if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { throw new \TypeError(sprintf('Argument 8 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); } diff --git a/src/Symfony/Component/Validator/composer.json b/src/Symfony/Component/Validator/composer.json index f221cc585f05e..ea412b89879a8 100644 --- a/src/Symfony/Component/Validator/composer.json +++ b/src/Symfony/Component/Validator/composer.json @@ -22,19 +22,19 @@ "symfony/translation-contracts": "^1.1" }, "require-dev": { - "symfony/http-client": "^4.3", - "symfony/http-foundation": "~4.1", - "symfony/http-kernel": "~3.4|~4.0", - "symfony/var-dumper": "~3.4|~4.0", - "symfony/intl": "^4.3", - "symfony/yaml": "~3.4|~4.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/cache": "~3.4|~4.0", - "symfony/property-access": "~3.4|~4.0", - "symfony/property-info": "~3.4|~4.0", - "symfony/translation": "~4.2", + "symfony/http-client": "^4.3|^5.0", + "symfony/http-foundation": "^4.1|^5.0", + "symfony/http-kernel": "^3.4|^4.0|^5.0", + "symfony/var-dumper": "^3.4|^4.0|^5.0", + "symfony/intl": "^4.3|^5.0", + "symfony/yaml": "^3.4|^4.0|^5.0", + "symfony/config": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/expression-language": "^3.4|^4.0|^5.0", + "symfony/cache": "^3.4|^4.0|^5.0", + "symfony/property-access": "^3.4|^4.0|^5.0", + "symfony/property-info": "^3.4|^4.0|^5.0", + "symfony/translation": "^4.2", "doctrine/annotations": "~1.0", "doctrine/cache": "~1.0", "egulias/email-validator": "^1.2.8|~2.0" @@ -44,7 +44,7 @@ "symfony/dependency-injection": "<3.4", "symfony/http-kernel": "<3.4", "symfony/intl": "<4.3", - "symfony/translation": "<4.2", + "symfony/translation": ">=5.0", "symfony/yaml": "<3.4" }, "suggest": { @@ -70,7 +70,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/VarDumper/Caster/SymfonyCaster.php b/src/Symfony/Component/VarDumper/Caster/SymfonyCaster.php index 78acb90b66a68..aa10465861296 100644 --- a/src/Symfony/Component/VarDumper/Caster/SymfonyCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/SymfonyCaster.php @@ -48,4 +48,16 @@ public static function castHttpClient($client, array $a, Stub $stub, $isNested) return $a; } + + public static function castHttpClientResponse($response, array $a, Stub $stub, $isNested) + { + $stub->cut += \count($a); + $a = []; + + foreach ($response->getInfo() + ['debug' => $response->getInfo('debug')] as $k => $v) { + $a[Caster::PREFIX_VIRTUAL.$k] = $v; + } + + return $a; + } } diff --git a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php index aacab2c8853bf..ea2b45ffa1a06 100644 --- a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php +++ b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php @@ -78,8 +78,8 @@ abstract class AbstractCloner implements ClonerInterface 'Symfony\Component\DependencyInjection\ContainerInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'Symfony\Component\HttpClient\CurlHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'], 'Symfony\Component\HttpClient\NativeHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'], - 'Symfony\Component\HttpClient\Response\CurlResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'], - 'Symfony\Component\HttpClient\Response\NativeResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'], + 'Symfony\Component\HttpClient\Response\CurlResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'], + 'Symfony\Component\HttpClient\Response\NativeResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'], 'Symfony\Component\HttpFoundation\Request' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castRequest'], 'Symfony\Component\VarDumper\Exception\ThrowingCasterException' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castThrowingCasterException'], 'Symfony\Component\VarDumper\Caster\TraceStub' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castTraceStub'], diff --git a/src/Symfony/Component/VarDumper/composer.json b/src/Symfony/Component/VarDumper/composer.json index b0c0273788ac0..727e50f3cfbb0 100644 --- a/src/Symfony/Component/VarDumper/composer.json +++ b/src/Symfony/Component/VarDumper/composer.json @@ -22,8 +22,8 @@ }, "require-dev": { "ext-iconv": "*", - "symfony/console": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0", + "symfony/console": "^3.4|^4.0|^5.0", + "symfony/process": "^3.4|^4.0|^5.0", "twig/twig": "~1.34|~2.4" }, "conflict": { @@ -48,7 +48,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/VarExporter/composer.json b/src/Symfony/Component/VarExporter/composer.json index 3d543df671a54..6b7dba24dedbc 100644 --- a/src/Symfony/Component/VarExporter/composer.json +++ b/src/Symfony/Component/VarExporter/composer.json @@ -19,7 +19,7 @@ "php": "^7.1.3" }, "require-dev": { - "symfony/var-dumper": "^4.1.1" + "symfony/var-dumper": "^4.1.1|^5.0" }, "autoload": { "psr-4": { "Symfony\\Component\\VarExporter\\": "" }, @@ -30,7 +30,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/WebLink/composer.json b/src/Symfony/Component/WebLink/composer.json index 14a519d7d0cf9..fa68a919098a7 100644 --- a/src/Symfony/Component/WebLink/composer.json +++ b/src/Symfony/Component/WebLink/composer.json @@ -24,8 +24,8 @@ "symfony/http-kernel": "" }, "require-dev": { - "symfony/http-foundation": "~3.4|~4.0", - "symfony/http-kernel": "^4.3" + "symfony/http-foundation": "^3.4|^4.0|^5.0", + "symfony/http-kernel": "^4.3|^5.0" }, "conflict": { "symfony/http-kernel": "<4.3" @@ -39,7 +39,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Workflow/composer.json b/src/Symfony/Component/Workflow/composer.json index 42a7e0cab7e1b..212be78d02e48 100644 --- a/src/Symfony/Component/Workflow/composer.json +++ b/src/Symfony/Component/Workflow/composer.json @@ -21,18 +21,19 @@ ], "require": { "php": "^7.1.3", - "symfony/property-access": "~3.4|~4.0" + "symfony/property-access": "^3.4|^4.0|^5.0" }, "require-dev": { "psr/log": "~1.0", - "symfony/dependency-injection": "~3.4|~4.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", "symfony/event-dispatcher": "^4.3", - "symfony/expression-language": "~3.4|~4.0", - "symfony/security-core": "~3.4|~4.0", - "symfony/validator": "~3.4|~4.0" + "symfony/expression-language": "^3.4|^4.0|^5.0", + "symfony/security-core": "^3.4|^4.0", + "symfony/validator": "^3.4|^4.0|^5.0" }, "conflict": { - "symfony/event-dispatcher": "<4.3" + "symfony/event-dispatcher": "<4.3|>=5", + "symfony/security-core": ">=5" }, "autoload": { "psr-4": { "Symfony\\Component\\Workflow\\": "" } @@ -40,7 +41,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Component/Yaml/composer.json b/src/Symfony/Component/Yaml/composer.json index 2338728efecfc..407b297c2f24b 100644 --- a/src/Symfony/Component/Yaml/composer.json +++ b/src/Symfony/Component/Yaml/composer.json @@ -20,7 +20,7 @@ "symfony/polyfill-ctype": "~1.8" }, "require-dev": { - "symfony/console": "~3.4|~4.0" + "symfony/console": "^3.4|^4.0|^5.0" }, "conflict": { "symfony/console": "<3.4" @@ -37,7 +37,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } } } diff --git a/src/Symfony/Contracts/Cache/composer.json b/src/Symfony/Contracts/Cache/composer.json index 4e0bd1a422700..e30b0bca8f7ea 100644 --- a/src/Symfony/Contracts/Cache/composer.json +++ b/src/Symfony/Contracts/Cache/composer.json @@ -28,7 +28,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "1.2-dev" } } } diff --git a/src/Symfony/Contracts/EventDispatcher/composer.json b/src/Symfony/Contracts/EventDispatcher/composer.json index 55802a491da69..8251d90a0cb45 100644 --- a/src/Symfony/Contracts/EventDispatcher/composer.json +++ b/src/Symfony/Contracts/EventDispatcher/composer.json @@ -28,7 +28,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "1.2-dev" } } } diff --git a/src/Symfony/Contracts/HttpClient/composer.json b/src/Symfony/Contracts/HttpClient/composer.json index 4dc9b2d38ce1e..718852d5c0a18 100644 --- a/src/Symfony/Contracts/HttpClient/composer.json +++ b/src/Symfony/Contracts/HttpClient/composer.json @@ -27,7 +27,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "1.2-dev" } } } diff --git a/src/Symfony/Contracts/Service/composer.json b/src/Symfony/Contracts/Service/composer.json index f4209cc41c48f..d8738fa213fe6 100644 --- a/src/Symfony/Contracts/Service/composer.json +++ b/src/Symfony/Contracts/Service/composer.json @@ -28,7 +28,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "1.2-dev" } } } diff --git a/src/Symfony/Contracts/Translation/composer.json b/src/Symfony/Contracts/Translation/composer.json index 09749d35f585a..22ef38363eb38 100644 --- a/src/Symfony/Contracts/Translation/composer.json +++ b/src/Symfony/Contracts/Translation/composer.json @@ -27,7 +27,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "1.2-dev" } } } diff --git a/src/Symfony/Contracts/composer.json b/src/Symfony/Contracts/composer.json index b9277e1dd1615..a3fa4afd98924 100644 --- a/src/Symfony/Contracts/composer.json +++ b/src/Symfony/Contracts/composer.json @@ -47,7 +47,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "1.2-dev" } } }