From 8d1f32613ba1296f0766f34166e399a442d76912 Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Tue, 19 Nov 2019 17:14:53 +0100 Subject: [PATCH 01/38] [Validator] ConstraintValidatorTestCase: add missing return value to mocked validate method calls --- .../Validator/Test/ConstraintValidatorTestCase.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php b/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php index 18705b7987..ed57d396ae 100644 --- a/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php +++ b/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php @@ -177,7 +177,8 @@ abstract class ConstraintValidatorTestCase extends TestCase ->willReturn($validator); $validator->expects($this->at(2 * $i + 1)) ->method('validate') - ->with($value, $this->logicalOr(null, [], $this->isInstanceOf('\Symfony\Component\Validator\Constraints\Valid')), $group); + ->with($value, $this->logicalOr(null, [], $this->isInstanceOf('\Symfony\Component\Validator\Constraints\Valid')), $group) + ->willReturn($validator); } protected function expectValidateValueAt($i, $propertyPath, $value, $constraints, $group = null) @@ -189,7 +190,8 @@ abstract class ConstraintValidatorTestCase extends TestCase ->willReturn($contextualValidator); $contextualValidator->expects($this->at(2 * $i + 1)) ->method('validate') - ->with($value, $constraints, $group); + ->with($value, $constraints, $group) + ->willReturn($contextualValidator); } protected function assertNoViolation() From 90cb32a044777b8499ab12e4d10d2e00d3eea024 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 21 Nov 2019 08:07:51 +0100 Subject: [PATCH 02/38] Update CHANGELOG for 4.4.0 --- CHANGELOG-4.4.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG-4.4.md b/CHANGELOG-4.4.md index 0f975d9e83..64e77d4a4d 100644 --- a/CHANGELOG-4.4.md +++ b/CHANGELOG-4.4.md @@ -7,6 +7,13 @@ in 4.4 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v4.4.0...v4.4.1 +* 4.4.0 (2019-11-21) + + * bug #34464 [Form] group constraints when calling the validator (nicolas-grekas) + * bug #34451 [DependencyInjection] Fix dumping multiple deprecated aliases (shyim) + * bug #34448 [Form] allow button names to start with uppercase letter (xabbuh) + * bug #34428 [Security] Fix best encoder not wired using migrate_from (chalasr) + * 4.4.0-RC1 (2019-11-17) * bug #34419 [Cache] Disable igbinary on PHP >= 7.4 (nicolas-grekas) From 65ec3b47e8d05eebc7300ac3d28c04f451472b67 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 21 Nov 2019 08:08:15 +0100 Subject: [PATCH 03/38] Update VERSION for 4.4.0 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 47f0b49b58..d965080ae4 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -76,12 +76,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private static $freshCache = []; - const VERSION = '4.4.0-DEV'; + const VERSION = '4.4.0'; const VERSION_ID = 40400; const MAJOR_VERSION = 4; const MINOR_VERSION = 4; const RELEASE_VERSION = 0; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '11/2022'; const END_OF_LIFE = '11/2023'; From a7fe2b29d2508090ac67b494788abec25a280cb6 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 21 Nov 2019 08:13:22 +0100 Subject: [PATCH 04/38] Bump Symfony version to 4.4.1 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index d965080ae4..c706ea6e1b 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -76,12 +76,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private static $freshCache = []; - const VERSION = '4.4.0'; - const VERSION_ID = 40400; + const VERSION = '4.4.1-DEV'; + const VERSION_ID = 40401; const MAJOR_VERSION = 4; const MINOR_VERSION = 4; - const RELEASE_VERSION = 0; - const EXTRA_VERSION = ''; + const RELEASE_VERSION = 1; + const EXTRA_VERSION = 'DEV'; const END_OF_MAINTENANCE = '11/2022'; const END_OF_LIFE = '11/2023'; From b435b1aab6bd19c5b9b2d3eb9ef6919f97ce83ae Mon Sep 17 00:00:00 2001 From: "tien.xuan.vo" Date: Thu, 21 Nov 2019 16:23:19 +0700 Subject: [PATCH 05/38] [Messenger] Ignore stamps in in-memory transport --- .../Tests/Transport/InMemoryTransportTest.php | 14 ++++++++++++++ .../Messenger/Transport/InMemoryTransport.php | 6 +++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Messenger/Tests/Transport/InMemoryTransportTest.php b/src/Symfony/Component/Messenger/Tests/Transport/InMemoryTransportTest.php index 22149f8a39..6fddc3fbbc 100644 --- a/src/Symfony/Component/Messenger/Tests/Transport/InMemoryTransportTest.php +++ b/src/Symfony/Component/Messenger/Tests/Transport/InMemoryTransportTest.php @@ -13,6 +13,7 @@ namespace Symfony\Component\Messenger\Tests\Transport; use PHPUnit\Framework\TestCase; use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Tests\Fixtures\AnEnvelopeStamp; use Symfony\Component\Messenger\Transport\InMemoryTransport; /** @@ -50,6 +51,19 @@ class InMemoryTransportTest extends TestCase $this->assertSame([], $this->transport->get()); } + public function testAcknowledgeSameMessageWithDifferentStamps() + { + $envelope1 = new Envelope(new \stdClass(), [new AnEnvelopeStamp()]); + $this->transport->send($envelope1); + $envelope2 = new Envelope(new \stdClass(), [new AnEnvelopeStamp()]); + $this->transport->send($envelope2); + $this->assertSame([$envelope1, $envelope2], $this->transport->get()); + $this->transport->ack($envelope1->with(new AnEnvelopeStamp())); + $this->assertSame([$envelope2], $this->transport->get()); + $this->transport->reject($envelope2->with(new AnEnvelopeStamp())); + $this->assertSame([], $this->transport->get()); + } + public function testAck() { $envelope = new Envelope(new \stdClass()); diff --git a/src/Symfony/Component/Messenger/Transport/InMemoryTransport.php b/src/Symfony/Component/Messenger/Transport/InMemoryTransport.php index 354bb601a1..09cbb31a04 100644 --- a/src/Symfony/Component/Messenger/Transport/InMemoryTransport.php +++ b/src/Symfony/Component/Messenger/Transport/InMemoryTransport.php @@ -55,7 +55,7 @@ class InMemoryTransport implements TransportInterface, ResetInterface public function ack(Envelope $envelope): void { $this->acknowledged[] = $envelope; - $id = spl_object_hash($envelope); + $id = spl_object_hash($envelope->getMessage()); unset($this->queue[$id]); } @@ -65,7 +65,7 @@ class InMemoryTransport implements TransportInterface, ResetInterface public function reject(Envelope $envelope): void { $this->rejected[] = $envelope; - $id = spl_object_hash($envelope); + $id = spl_object_hash($envelope->getMessage()); unset($this->queue[$id]); } @@ -75,7 +75,7 @@ class InMemoryTransport implements TransportInterface, ResetInterface public function send(Envelope $envelope): Envelope { $this->sent[] = $envelope; - $id = spl_object_hash($envelope); + $id = spl_object_hash($envelope->getMessage()); $this->queue[$id] = $envelope; return $envelope; From 59d677182e096e4ff7ede2fd195433f977d6cc1f Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Thu, 21 Nov 2019 23:18:50 +0100 Subject: [PATCH 06/38] [FrameworkBundle][ContainerLint] Keep removing compiler passes --- .../Bundle/FrameworkBundle/Command/ContainerLintCommand.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php index c5cba6c9a5..cba4ac5840 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php @@ -70,7 +70,6 @@ final class ContainerLintCommand extends Command if (!$kernel->isDebug() || !(new ConfigCache($kernel->getContainer()->getParameter('debug.container.dump'), true))->isFresh()) { $buildContainer = \Closure::bind(function () { return $this->buildContainer(); }, $kernel, \get_class($kernel)); $container = $buildContainer(); - $container->getCompilerPassConfig()->setRemovingPasses([]); } else { (new XmlFileLoader($container = new ContainerBuilder(), new FileLocator()))->load($kernel->getContainer()->getParameter('debug.container.dump')); } From bedad35e59bf7f6834ddb0fd75b21f3dd7a16d25 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 22 Nov 2019 13:40:21 +0100 Subject: [PATCH 07/38] remove return type declaration from __sleep() --- src/Symfony/Component/Validator/Constraint.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Validator/Constraint.php b/src/Symfony/Component/Validator/Constraint.php index 569ccfa93d..671e97e9c3 100644 --- a/src/Symfony/Component/Validator/Constraint.php +++ b/src/Symfony/Component/Validator/Constraint.php @@ -288,7 +288,7 @@ abstract class Constraint * * @internal */ - public function __sleep(): array + public function __sleep() { // Initialize "groups" option if it is not set $this->groups; From 0acaa5cfb36f7c7f1dc284d4fd338a9353c1c0e3 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 22 Nov 2019 13:53:53 +0100 Subject: [PATCH 08/38] definitions are valid objects --- .../Compiler/CheckTypeDeclarationsPass.php | 4 ++++ .../Compiler/CheckTypeDeclarationsPassTest.php | 14 ++++++++++++++ .../CheckTypeDeclarationsPass/FooObject.php | 10 ++++++++++ 3 files changed, 28 insertions(+) create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/FooObject.php diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php index 2147d53f12..ccd11e9d48 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php @@ -152,6 +152,10 @@ final class CheckTypeDeclarationsPass extends AbstractRecursivePass return; } + if ('object' === $type) { + return; + } + if (is_a($class, $type, true)) { return; } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php index 51bc7c6779..40f65f0e31 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php @@ -15,12 +15,14 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Compiler\CheckTypeDeclarationsPass; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\Bar; use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\BarMethodCall; use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\BarOptionalArgument; use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\BarOptionalArgumentNotNull; use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\Foo; +use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\FooObject; /** * @author Nicolas Grekas @@ -390,6 +392,18 @@ class CheckTypeDeclarationsPassTest extends TestCase $this->addToAssertionCount(1); } + public function testProcessSuccessWhenPassingDefintionForObjectType() + { + $container = new ContainerBuilder(); + + $container->register('foo_object', FooObject::class) + ->addArgument(new Definition(Foo::class)); + + (new CheckTypeDeclarationsPass(true))->process($container); + + $this->addToAssertionCount(1); + } + public function testProcessFactory() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/FooObject.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/FooObject.php new file mode 100644 index 0000000000..44cfaf0d0a --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/FooObject.php @@ -0,0 +1,10 @@ + Date: Fri, 22 Nov 2019 14:14:59 +0100 Subject: [PATCH 09/38] drop return type declaration The configured memory limit can exceed the range that can be represented by an integer. --- .../HttpKernel/DataCollector/MemoryDataCollector.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/DataCollector/MemoryDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/MemoryDataCollector.php index 59758428c6..7ffcdab41d 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/MemoryDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/MemoryDataCollector.php @@ -93,7 +93,10 @@ class MemoryDataCollector extends DataCollector implements LateDataCollectorInte return 'memory'; } - private function convertToBytes(string $memoryLimit): int + /** + * @return int|float + */ + private function convertToBytes(string $memoryLimit) { if ('-1' === $memoryLimit) { return -1; From 6ff20f01228c14392023a7a81c1e469121c30e2f Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 22 Nov 2019 14:37:01 +0100 Subject: [PATCH 10/38] compatibility with DoctrineBundle 2 --- .../Transport/Doctrine/DoctrineTransportFactory.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Messenger/Transport/Doctrine/DoctrineTransportFactory.php b/src/Symfony/Component/Messenger/Transport/Doctrine/DoctrineTransportFactory.php index 959a4ec7a2..caa13082d9 100644 --- a/src/Symfony/Component/Messenger/Transport/Doctrine/DoctrineTransportFactory.php +++ b/src/Symfony/Component/Messenger/Transport/Doctrine/DoctrineTransportFactory.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Messenger\Transport\Doctrine; +use Doctrine\Common\Persistence\ConnectionRegistry; use Symfony\Bridge\Doctrine\RegistryInterface; use Symfony\Component\Messenger\Exception\TransportException; use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface; @@ -26,8 +27,12 @@ class DoctrineTransportFactory implements TransportFactoryInterface { private $registry; - public function __construct(RegistryInterface $registry) + public function __construct($registry) { + if (!$registry instanceof RegistryInterface && !$registry instanceof ConnectionRegistry) { + throw new \TypeError(sprintf('Expected an instance of %s or %s, but got %s.', RegistryInterface::class, ConnectionRegistry::class, \is_object($registry) ? \get_class($registry) : \gettype($registry))); + } + $this->registry = $registry; } From 7425d2c69d7dcf35ab0a5d574951d074f7ec547f Mon Sep 17 00:00:00 2001 From: elementaire Date: Fri, 22 Nov 2019 15:36:52 +0100 Subject: [PATCH 11/38] Fix error message according to the new regex Complete https://github.com/symfony/symfony/pull/34448 --- src/Symfony/Component/Form/ButtonBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Form/ButtonBuilder.php b/src/Symfony/Component/Form/ButtonBuilder.php index ca6bf78dc9..0665c589c9 100644 --- a/src/Symfony/Component/Form/ButtonBuilder.php +++ b/src/Symfony/Component/Form/ButtonBuilder.php @@ -63,7 +63,7 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface if (preg_match('/^([^a-zA-Z0-9_].*)?(.*[^a-zA-Z0-9_\-:].*)?$/D', $name, $matches)) { if (isset($matches[1])) { - @trigger_error(sprintf('Using names for buttons that do not start with a lowercase letter, a digit, or an underscore is deprecated since Symfony 4.3 and will throw an exception in 5.0 ("%s" given).', $name), E_USER_DEPRECATED); + @trigger_error(sprintf('Using names for buttons that do not start with a letter, a digit, or an underscore is deprecated since Symfony 4.3 and will throw an exception in 5.0 ("%s" given).', $name), E_USER_DEPRECATED); } if (isset($matches[2])) { @trigger_error(sprintf('Using names for buttons that do not contain only letters, digits, underscores ("_"), hyphens ("-") and colons (":") ("%s" given) is deprecated since Symfony 4.3 and will throw an exception in 5.0.', $name), E_USER_DEPRECATED); From f5bd42159719a3a1221d35c0dfd9937879765b52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandru=20N=C4=83stase?= Date: Sat, 23 Nov 2019 11:53:09 +0100 Subject: [PATCH 12/38] Add missing defaultPriorityMethod field in TaggedIteratorArgument --- .../DependencyInjection/Argument/TaggedIteratorArgument.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Symfony/Component/DependencyInjection/Argument/TaggedIteratorArgument.php b/src/Symfony/Component/DependencyInjection/Argument/TaggedIteratorArgument.php index 1a2518812f..d1d5f6d8a4 100644 --- a/src/Symfony/Component/DependencyInjection/Argument/TaggedIteratorArgument.php +++ b/src/Symfony/Component/DependencyInjection/Argument/TaggedIteratorArgument.php @@ -21,6 +21,7 @@ class TaggedIteratorArgument extends IteratorArgument private $tag; private $indexAttribute; private $defaultIndexMethod; + private $defaultPriorityMethod; private $needsIndexes = false; /** From 2bf6cd2eea53fe8acff38b76483d7049655dafb1 Mon Sep 17 00:00:00 2001 From: Tugdual Saunier Date: Sat, 23 Nov 2019 15:44:33 +0100 Subject: [PATCH 13/38] [Security] Fix SwitchUser is broken when the User Provider always returns a valid user --- .../Component/Security/Http/Firewall/SwitchUserListener.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php b/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php index e383b48768..4b82fa1f8c 100644 --- a/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php @@ -153,7 +153,6 @@ class SwitchUserListener implements ListenerInterface try { $this->provider->loadUserByUsername($nonExistentUsername); - throw new \LogicException('AuthenticationException expected'); } catch (AuthenticationException $e) { } } catch (AuthenticationException $e) { From 8026609dc906d1313f0e943896142555ddac3568 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 23 Nov 2019 15:53:46 +0100 Subject: [PATCH 14/38] don't fail when referenced env var does not exist --- src/Symfony/Component/Dotenv/Dotenv.php | 2 +- src/Symfony/Component/Dotenv/Tests/DotenvTest.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Dotenv/Dotenv.php b/src/Symfony/Component/Dotenv/Dotenv.php index de40ade592..81bb08810e 100644 --- a/src/Symfony/Component/Dotenv/Dotenv.php +++ b/src/Symfony/Component/Dotenv/Dotenv.php @@ -466,7 +466,7 @@ final class Dotenv $value = ''; } - if ('' === $value && isset($matches['default_value'])) { + if ('' === $value && isset($matches['default_value']) && '' !== $matches['default_value']) { $unsupportedChars = strpbrk($matches['default_value'], '\'"{$'); if (false !== $unsupportedChars) { throw $this->createFormatException(sprintf('Unsupported character "%s" found in the default value of variable "$%s".', $unsupportedChars[0], $name)); diff --git a/src/Symfony/Component/Dotenv/Tests/DotenvTest.php b/src/Symfony/Component/Dotenv/Tests/DotenvTest.php index 3f232698ae..d4c7c53dcd 100644 --- a/src/Symfony/Component/Dotenv/Tests/DotenvTest.php +++ b/src/Symfony/Component/Dotenv/Tests/DotenvTest.php @@ -172,6 +172,7 @@ class DotenvTest extends TestCase ["FOO=BAR\nBAR=\${NOTDEFINED:=TEST}", ['FOO' => 'BAR', 'NOTDEFINED' => 'TEST', 'BAR' => 'TEST']], ["FOO=\nBAR=\${FOO:=TEST}", ['FOO' => 'TEST', 'BAR' => 'TEST']], ["FOO=\nBAR=\$FOO:=TEST}", ['FOO' => 'TEST', 'BAR' => 'TEST}']], + ["FOO=foo\nFOOBAR=\${FOO}\${BAR}", ['FOO' => 'foo', 'FOOBAR' => 'foo']], ]; if ('\\' !== \DIRECTORY_SEPARATOR) { From 960faef66f48d71d04d605c9067d05ba95dc7585 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Thu, 14 Nov 2019 23:18:30 +0100 Subject: [PATCH 15/38] Avoid empty \"If-Modified-Since\" header in validation request --- src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php | 4 +++- .../Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php index addeca8bae..dbe0280659 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php @@ -352,7 +352,9 @@ class HttpCache implements HttpKernelInterface, TerminableInterface } // add our cached last-modified validator - $subRequest->headers->set('if_modified_since', $entry->headers->get('Last-Modified')); + if ($entry->headers->has('Last-Modified')) { + $subRequest->headers->set('if_modified_since', $entry->headers->get('Last-Modified')); + } // Add our cached etag validator to the environment. // We keep the etags from the client to handle the case when the client diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php index a50e09fb14..93d92eb11e 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php @@ -859,6 +859,7 @@ class HttpCacheTest extends HttpCacheTestCase public function testValidatesCachedResponsesWithETagAndNoFreshnessInformation() { $this->setNextResponse(200, [], 'Hello World', function ($request, $response) { + $this->assertFalse($request->headers->has('If-Modified-Since')); $response->headers->set('Cache-Control', 'public'); $response->headers->set('ETag', '"12345"'); if ($response->getETag() == $request->headers->get('IF_NONE_MATCH')) { From 026730e913ef09522a2e57a07147e3d740f13e7a Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Sat, 23 Nov 2019 21:28:49 +0100 Subject: [PATCH 16/38] Remove some unused methods parameters --- .../Console/Descriptor/JsonDescriptor.php | 4 ++-- .../DependencyInjection/SecurityExtension.php | 4 ++-- .../Bundle/WebServerBundle/Command/ServerLogCommand.php | 4 ++-- .../DependencyInjection/Loader/XmlFileLoader.php | 9 ++++----- .../Component/Translation/Dumper/XliffFileDumper.php | 4 ++-- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php index ea5d0c7d0b..197db657f0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php @@ -166,7 +166,7 @@ class JsonDescriptor extends Descriptor */ protected function describeCallable($callable, array $options = []) { - $this->writeData($this->getCallableData($callable, $options), $options); + $this->writeData($this->getCallableData($callable), $options); } /** @@ -321,7 +321,7 @@ class JsonDescriptor extends Descriptor * * @return array */ - private function getCallableData($callable, array $options = []) + private function getCallableData($callable) { $data = []; diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 731ce42d39..795fe053e6 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -585,7 +585,7 @@ class SecurityExtension extends Extension { $encoderMap = []; foreach ($encoders as $class => $encoder) { - $encoderMap[$class] = $this->createEncoder($encoder, $container); + $encoderMap[$class] = $this->createEncoder($encoder); } $container @@ -594,7 +594,7 @@ class SecurityExtension extends Extension ; } - private function createEncoder($config, ContainerBuilder $container) + private function createEncoder($config) { // a custom encoder service if (isset($config['id'])) { diff --git a/src/Symfony/Bundle/WebServerBundle/Command/ServerLogCommand.php b/src/Symfony/Bundle/WebServerBundle/Command/ServerLogCommand.php index 73c51cc8e0..5c9ce910dd 100644 --- a/src/Symfony/Bundle/WebServerBundle/Command/ServerLogCommand.php +++ b/src/Symfony/Bundle/WebServerBundle/Command/ServerLogCommand.php @@ -114,7 +114,7 @@ EOF continue; } - $this->displayLog($input, $output, $clientId, $record); + $this->displayLog($output, $clientId, $record); } } @@ -141,7 +141,7 @@ EOF } } - private function displayLog(InputInterface $input, OutputInterface $output, $clientId, array $record) + private function displayLog(OutputInterface $output, $clientId, array $record) { if ($this->handler->isHandling($record)) { if (isset($record['log_id'])) { diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index c4b9b69a03..bd2902f856 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -278,7 +278,7 @@ class XmlFileLoader extends FileLoader $definition->setDeprecated(true, $deprecated[0]->nodeValue ?: null); } - $definition->setArguments($this->getArgumentsAsPhp($service, 'argument', $file, false, $definition instanceof ChildDefinition)); + $definition->setArguments($this->getArgumentsAsPhp($service, 'argument', $file, $definition instanceof ChildDefinition)); $definition->setProperties($this->getArgumentsAsPhp($service, 'property', $file)); if ($factories = $this->getChildren($service, 'factory')) { @@ -452,11 +452,10 @@ class XmlFileLoader extends FileLoader * * @param string $name * @param string $file - * @param bool $lowercase * * @return mixed */ - private function getArgumentsAsPhp(\DOMElement $node, $name, $file, $lowercase = true, $isChildDefinition = false) + private function getArgumentsAsPhp(\DOMElement $node, $name, $file, $isChildDefinition = false) { $arguments = []; foreach ($this->getChildren($node, $name) as $arg) { @@ -506,10 +505,10 @@ class XmlFileLoader extends FileLoader $arguments[$key] = new Expression($arg->nodeValue); break; case 'collection': - $arguments[$key] = $this->getArgumentsAsPhp($arg, $name, $file, false); + $arguments[$key] = $this->getArgumentsAsPhp($arg, $name, $file); break; case 'iterator': - $arg = $this->getArgumentsAsPhp($arg, $name, $file, false); + $arg = $this->getArgumentsAsPhp($arg, $name, $file); try { $arguments[$key] = new IteratorArgument($arg); } catch (InvalidArgumentException $e) { diff --git a/src/Symfony/Component/Translation/Dumper/XliffFileDumper.php b/src/Symfony/Component/Translation/Dumper/XliffFileDumper.php index cd867b0967..f933be8b25 100644 --- a/src/Symfony/Component/Translation/Dumper/XliffFileDumper.php +++ b/src/Symfony/Component/Translation/Dumper/XliffFileDumper.php @@ -41,7 +41,7 @@ class XliffFileDumper extends FileDumper return $this->dumpXliff1($defaultLocale, $messages, $domain, $options); } if ('2.0' === $xliffVersion) { - return $this->dumpXliff2($defaultLocale, $messages, $domain, $options); + return $this->dumpXliff2($defaultLocale, $messages, $domain); } throw new InvalidArgumentException(sprintf('No support implemented for dumping XLIFF version "%s".', $xliffVersion)); @@ -129,7 +129,7 @@ class XliffFileDumper extends FileDumper return $dom->saveXML(); } - private function dumpXliff2($defaultLocale, MessageCatalogue $messages, $domain, array $options = []) + private function dumpXliff2($defaultLocale, MessageCatalogue $messages, $domain) { $dom = new \DOMDocument('1.0', 'utf-8'); $dom->formatOutput = true; From 2ff3496d620425bbf7b7c31e7a6eceb512cc6dc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Sun, 24 Nov 2019 18:18:02 +0100 Subject: [PATCH 17/38] [Workflow] Apply the same logic of precedence between the apply() and the buildTransitionBlockerList() method --- .../Workflow/Tests/StateMachineTest.php | 38 ++++++++++++--- src/Symfony/Component/Workflow/Workflow.php | 46 ++++++++++++------- 2 files changed, 62 insertions(+), 22 deletions(-) diff --git a/src/Symfony/Component/Workflow/Tests/StateMachineTest.php b/src/Symfony/Component/Workflow/Tests/StateMachineTest.php index 9224f7cb12..a6c7362f79 100644 --- a/src/Symfony/Component/Workflow/Tests/StateMachineTest.php +++ b/src/Symfony/Component/Workflow/Tests/StateMachineTest.php @@ -5,6 +5,7 @@ namespace Symfony\Component\Workflow\Tests; use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\Workflow\Event\GuardEvent; +use Symfony\Component\Workflow\Exception\NotEnabledTransitionException; use Symfony\Component\Workflow\StateMachine; use Symfony\Component\Workflow\TransitionBlocker; @@ -84,27 +85,52 @@ class StateMachineTest extends TestCase $subject = new Subject(); // There may be multiple transitions with the same name. Make sure that transitions - // that are not enabled by the marking are evaluated. + // that are enabled by the marking are evaluated. // see https://github.com/symfony/symfony/issues/28432 - // Test if when you are in place "a"trying transition "t1" then returned + // Test if when you are in place "a" and trying to apply "t1" then it returns // blocker list contains guard blocker instead blockedByMarking $subject->setMarking('a'); $transitionBlockerList = $net->buildTransitionBlockerList($subject, 't1'); $this->assertCount(1, $transitionBlockerList); $blockers = iterator_to_array($transitionBlockerList); - $this->assertSame('Transition blocker of place a', $blockers[0]->getMessage()); $this->assertSame('blocker', $blockers[0]->getCode()); - // Test if when you are in place "d" trying transition "t1" then - // returned blocker list contains guard blocker instead blockedByMarking + // Test if when you are in place "d" and trying to apply "t1" then + // it returns blocker list contains guard blocker instead blockedByMarking $subject->setMarking('d'); $transitionBlockerList = $net->buildTransitionBlockerList($subject, 't1'); $this->assertCount(1, $transitionBlockerList); $blockers = iterator_to_array($transitionBlockerList); - $this->assertSame('Transition blocker of place d', $blockers[0]->getMessage()); $this->assertSame('blocker', $blockers[0]->getCode()); } + + public function testApplyReturnsExpectedReasonOnBranchMerge() + { + $definition = $this->createComplexStateMachineDefinition(); + + $dispatcher = new EventDispatcher(); + $net = new StateMachine($definition, null, $dispatcher); + + $dispatcher->addListener('workflow.guard', function (GuardEvent $event) { + $event->addTransitionBlocker(new TransitionBlocker(sprintf('Transition blocker of place %s', $event->getTransition()->getFroms()[0]), 'blocker')); + }); + + $subject = new Subject(); + + // There may be multiple transitions with the same name. Make sure that all transitions + // that are enabled by the marking are evaluated. + // see https://github.com/symfony/symfony/issues/34489 + + try { + $net->apply($subject, 't1'); + $this->fail(); + } catch (NotEnabledTransitionException $e) { + $blockers = iterator_to_array($e->getTransitionBlockerList()); + $this->assertSame('Transition blocker of place a', $blockers[0]->getMessage()); + $this->assertSame('blocker', $blockers[0]->getCode()); + } + } } diff --git a/src/Symfony/Component/Workflow/Workflow.php b/src/Symfony/Component/Workflow/Workflow.php index 2d56bd9ee5..53fef69274 100644 --- a/src/Symfony/Component/Workflow/Workflow.php +++ b/src/Symfony/Component/Workflow/Workflow.php @@ -159,25 +159,47 @@ class Workflow implements WorkflowInterface $marking = $this->getMarking($subject); - $transitionBlockerList = null; - $applied = false; - $approvedTransitionQueue = []; + $transitionExist = false; + $approvedTransitions = []; + $bestTransitionBlockerList = null; foreach ($this->definition->getTransitions() as $transition) { if ($transition->getName() !== $transitionName) { continue; } - $transitionBlockerList = $this->buildTransitionBlockerListForTransition($subject, $marking, $transition); - if (!$transitionBlockerList->isEmpty()) { + $transitionExist = true; + + $tmpTransitionBlockerList = $this->buildTransitionBlockerListForTransition($subject, $marking, $transition); + + if ($tmpTransitionBlockerList->isEmpty()) { + $approvedTransitions[] = $transition; continue; } - $approvedTransitionQueue[] = $transition; + + if (!$bestTransitionBlockerList) { + $bestTransitionBlockerList = $tmpTransitionBlockerList; + continue; + } + + // We prefer to return transitions blocker by something else than + // marking. Because it means the marking was OK. Transitions are + // deterministic: it's not possible to have many transitions enabled + // at the same time that match the same marking with the same name + if (!$tmpTransitionBlockerList->has(TransitionBlocker::BLOCKED_BY_MARKING)) { + $bestTransitionBlockerList = $tmpTransitionBlockerList; + } } - foreach ($approvedTransitionQueue as $transition) { - $applied = true; + if (!$transitionExist) { + throw new UndefinedTransitionException($subject, $transitionName, $this); + } + if (!$approvedTransitions) { + throw new NotEnabledTransitionException($subject, $transitionName, $this, $bestTransitionBlockerList); + } + + foreach ($approvedTransitions as $transition) { $this->leave($subject, $transition, $marking); $context = $this->transition($subject, $transition, $marking, $context); @@ -193,14 +215,6 @@ class Workflow implements WorkflowInterface $this->announce($subject, $transition, $marking); } - if (!$transitionBlockerList) { - throw new UndefinedTransitionException($subject, $transitionName, $this); - } - - if (!$applied) { - throw new NotEnabledTransitionException($subject, $transitionName, $this, $transitionBlockerList); - } - return $marking; } From 6cea35a10870de486c2001e3e2a0b0b3a96309ea Mon Sep 17 00:00:00 2001 From: "Issei.M" Date: Sun, 24 Nov 2019 16:00:50 +0900 Subject: [PATCH 18/38] [Validator] Add Japanese translation --- .../Validator/Resources/translations/validators.ja.xlf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.ja.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.ja.xlf index 5a391a2e66..21e2392c7d 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.ja.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.ja.xlf @@ -362,6 +362,10 @@ This password has been leaked in a data breach, it must not be used. Please use another password. このパスワードは漏洩している為使用できません。 + + This value should be between {{ min }} and {{ max }}. + {{ min }}以上{{ max }}以下でなければなりません。 + From dd92d2bb10157504f5da6f26460eca357ad0125b Mon Sep 17 00:00:00 2001 From: Valentin Udaltsov Date: Sat, 23 Nov 2019 13:39:00 +0100 Subject: [PATCH 19/38] [Messenger] Error when specified default bus is not among the configured --- .../DependencyInjection/Configuration.php | 5 +++++ .../DependencyInjection/ConfigurationTest.php | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 2b76352048..0f575b90d6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -19,6 +19,7 @@ use Symfony\Component\Asset\Package; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\Form\Form; use Symfony\Component\HttpClient\HttpClient; @@ -1136,6 +1137,10 @@ class Configuration implements ConfigurationInterface ->ifTrue(function ($v) { return isset($v['buses']) && \count($v['buses']) > 1 && null === $v['default_bus']; }) ->thenInvalid('You must specify the "default_bus" if you define more than one bus.') ->end() + ->validate() + ->ifTrue(static function ($v): bool { return isset($v['buses']) && null !== $v['default_bus'] && !isset($v['buses'][$v['default_bus']]); }) + ->then(static function (array $v): void { throw new InvalidConfigurationException(sprintf('The specified default bus "%s" is not configured. Available buses are "%s".', $v['default_bus'], implode('", "', array_keys($v['buses'])))); }) + ->end() ->children() ->arrayNode('routing') ->normalizeKeys(false) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index f673a65f42..e82c8e39bd 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -271,6 +271,27 @@ class ConfigurationTest extends TestCase ]); } + public function testItErrorsWhenDefaultBusDoesNotExist() + { + $processor = new Processor(); + $configuration = new Configuration(true); + + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage('The specified default bus "foo" is not configured. Available buses are "bar", "baz".'); + + $processor->processConfiguration($configuration, [ + [ + 'messenger' => [ + 'default_bus' => 'foo', + 'buses' => [ + 'bar' => null, + 'baz' => null, + ], + ], + ], + ]); + } + protected static function getBundleDefaultConfig() { return [ From 1a81dd7de80701d4cfe15e188030f81be67f116e Mon Sep 17 00:00:00 2001 From: jewome62 Date: Sat, 23 Nov 2019 13:13:59 +0100 Subject: [PATCH 20/38] Add DateTimeZoneNormalizer into Dependency Injection --- .../Bundle/FrameworkBundle/Resources/config/serializer.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml index 23da8b07bc..14a6ba9711 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml @@ -38,6 +38,11 @@ + + + + + From f46f3db058c7edd52f344b8b788d52a8e69f1072 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 24 Nov 2019 19:10:03 +0100 Subject: [PATCH 21/38] [FrameworkBundle] fix leftover mentioning "secret:" processor --- src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md | 2 +- .../Bundle/FrameworkBundle/Command/SecretsListCommand.php | 2 +- .../Bundle/FrameworkBundle/Command/SecretsSetCommand.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index 0220844b26..7a7e049dcf 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -18,7 +18,7 @@ CHANGELOG * Added new `error_controller` configuration to handle system exceptions * Added sort option for `translation:update` command. * [BC Break] The `framework.messenger.routing.senders` config key is not deeply merged anymore. - * Added `secrets:*` commands and `%env(secret:...)%` processor to deal with secrets seamlessly. + * Added `secrets:*` commands to deal with secrets seamlessly. * Made `framework.session.handler_id` accept a DSN * Marked the `RouterDataCollector` class as `@final`. * [BC Break] The `framework.messenger.buses..middleware` config key is not deeply merged anymore. diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsListCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsListCommand.php index 1b0fbdf4ce..cc322847d0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsListCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsListCommand.php @@ -64,7 +64,7 @@ EOF { $io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output); - $io->comment('Use "%env(secret:)%" to reference a secret in a config file.'); + $io->comment('Use "%env()%" to reference a secret in a config file.'); if (!$reveal = $input->getOption('reveal')) { $io->comment(sprintf('To reveal the secrets run php %s %s --reveal', $_SERVER['PHP_SELF'], $this->getName())); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsSetCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsSetCommand.php index 555d616712..5cca8d7011 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsSetCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsSetCommand.php @@ -56,7 +56,7 @@ The %command.name% command stores a secret in the vault. %command.full_name% To reference secrets in services.yaml or any other config -files, use "%env(secret:)%". +files, use "%env()%". By default, the secret value should be entered interactively. Alternatively, provide a file where to read the secret from: From db9754d9fca5cc9a2b54ab572c3c173aca8a15d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl?= Date: Thu, 21 Nov 2019 10:24:21 +0100 Subject: [PATCH 22/38] [Mailer] Add UPGRADE entries about Envelope and MessageEvent --- UPGRADE-4.4.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/UPGRADE-4.4.md b/UPGRADE-4.4.md index 1d1863e4f8..2dffdb1984 100644 --- a/UPGRADE-4.4.md +++ b/UPGRADE-4.4.md @@ -168,6 +168,8 @@ Mailer ------ * [BC BREAK] Changed the DSN to use for disabling delivery (using the `NullTransport`) from `smtp://null` to `null://null` (host doesn't matter). + * [BC BREAK] Renamed class `SmtpEnvelope` to `Envelope` and `DelayedSmtpEnvelope` to `DelayedEnvelope`. + * [BC BREAK] Added a required `string $transport` argument to `MessageEvent::__construct`. Messenger --------- From 465f092aeba45eb0c35882f566dc6e3c73d03b7a Mon Sep 17 00:00:00 2001 From: Antoine Makdessi Date: Tue, 19 Nov 2019 22:39:45 +0100 Subject: [PATCH 23/38] Improve Symfony description According to https://symfony.com/stats/downloads :) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5796b1acd7..da9e6156c0 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@

-[Symfony][1] is a **PHP framework** for web applications and a set of reusable +[Symfony][1] is a **PHP framework** for web and console applications and a set of reusable **PHP components**. Symfony is used by thousands of web applications (including BlaBlaCar.com and Spotify.com) and most of the [popular PHP projects][2] (including Drupal and Magento). From aa045d10b91f8290f553438900d9a732bec5d6ec Mon Sep 17 00:00:00 2001 From: Sander Toonen Date: Fri, 22 Nov 2019 22:50:07 +0100 Subject: [PATCH 24/38] [Monolog Bridge] Fixed accessing static property as non static. --- src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php b/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php index c27b0803e2..c8e21b2f42 100644 --- a/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php +++ b/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php @@ -39,7 +39,7 @@ class ChromePhpHandler extends BaseChromePhpHandler } if (!preg_match(static::USER_AGENT_REGEX, $event->getRequest()->headers->get('User-Agent'))) { - $this->sendHeaders = false; + self::$sendHeaders = false; $this->headers = []; return; @@ -57,7 +57,7 @@ class ChromePhpHandler extends BaseChromePhpHandler */ protected function sendHeader($header, $content) { - if (!$this->sendHeaders) { + if (!self::$sendHeaders) { return; } From 0950cfbc65bc0b9bbeb682eb237d028a4e3033c5 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Sat, 23 Nov 2019 05:18:35 +0100 Subject: [PATCH 25/38] [SecurityBundle] Don't require a user provider for the anonymous listener --- .../SecurityBundle/DependencyInjection/SecurityExtension.php | 4 ++-- .../Tests/DependencyInjection/SecurityExtensionTest.php | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 480768a6d0..627d7b92d5 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -464,8 +464,8 @@ class SecurityExtension extends Extension implements PrependExtensionInterface throw new InvalidConfigurationException(sprintf('Invalid firewall "%s": user provider "%s" not found.', $id, $firewall[$key]['provider'])); } $userProvider = $providerIds[$normalizedName]; - } elseif ('remember_me' === $key) { - // RememberMeFactory will use the firewall secret when created + } elseif ('remember_me' === $key || 'anonymous' === $key) { + // RememberMeFactory will use the firewall secret when created, AnonymousAuthenticationListener does not load users. $userProvider = null; } elseif ($defaultProvider) { $userProvider = $defaultProvider; diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php index 98624f7478..7c84f953a5 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php @@ -210,7 +210,7 @@ class SecurityExtensionTest extends TestCase $container->compile(); } - public function testPerListenerProviderWithRememberMe() + public function testPerListenerProviderWithRememberMeAndAnonymous() { $container = $this->getRawContainer(); $container->loadFromExtension('security', [ @@ -223,6 +223,7 @@ class SecurityExtensionTest extends TestCase 'default' => [ 'form_login' => ['provider' => 'second'], 'remember_me' => ['secret' => 'baz'], + 'anonymous' => true, ], ], ]); From f15e0e6a43f0048549a255c42229900290d64690 Mon Sep 17 00:00:00 2001 From: Christopher Hertel Date: Sun, 24 Nov 2019 19:49:09 +0100 Subject: [PATCH 26/38] [Messenger] Adding exception to amqp transport in case amqp ext is not installed --- .../Tests/Transport/AmqpExt/AmqpTransportFactoryTest.php | 3 +++ .../Component/Messenger/Transport/AmqpExt/Connection.php | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpTransportFactoryTest.php b/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpTransportFactoryTest.php index 60d5e806e3..b3cb7a6dc8 100644 --- a/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpTransportFactoryTest.php +++ b/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpTransportFactoryTest.php @@ -28,6 +28,9 @@ class AmqpTransportFactoryTest extends TestCase $this->assertFalse($factory->supports('invalid-dsn', [])); } + /** + * @requires extension amqp + */ public function testItCreatesTheTransport() { $factory = new AmqpTransportFactory(); diff --git a/src/Symfony/Component/Messenger/Transport/AmqpExt/Connection.php b/src/Symfony/Component/Messenger/Transport/AmqpExt/Connection.php index 267f1c67cd..de14741397 100644 --- a/src/Symfony/Component/Messenger/Transport/AmqpExt/Connection.php +++ b/src/Symfony/Component/Messenger/Transport/AmqpExt/Connection.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Messenger\Transport\AmqpExt; use Symfony\Component\Messenger\Exception\InvalidArgumentException; +use Symfony\Component\Messenger\Exception\LogicException; /** * An AMQP connection. @@ -60,6 +61,10 @@ class Connection public function __construct(array $connectionOptions, array $exchangeOptions, array $queuesOptions, AmqpFactory $amqpFactory = null) { + if (!\extension_loaded('amqp')) { + throw new LogicException(sprintf('You cannot use the "%s" as the "amqp" extension is not installed.', __CLASS__)); + } + $this->connectionOptions = array_replace_recursive([ 'delay' => [ 'exchange_name' => 'delays', From a0d6d30c9f92898d1003d9d2e55ee333ed6170ae Mon Sep 17 00:00:00 2001 From: Alexander Bauer Date: Sun, 24 Nov 2019 20:12:23 +0100 Subject: [PATCH 27/38] [FrameworkBundle][Cache] Don't deep-merge cache pools configuration --- .../Bundle/FrameworkBundle/DependencyInjection/Configuration.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index c99106299a..e6cf770e58 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -1014,6 +1014,7 @@ class Configuration implements ConfigurationInterface ->end() ->children() ->arrayNode('adapters') + ->performNoDeepMerging() ->info('One or more adapters to chain for creating the pool, defaults to "cache.app".') ->beforeNormalization() ->always()->then(function ($values) { From be80db3dc8e80214e64b7ee481dc01ef96bb1e17 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 25 Nov 2019 10:10:24 +0100 Subject: [PATCH 28/38] [HttpKernel] Don't cache "not-fresh" state --- src/Symfony/Component/HttpKernel/Kernel.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index c706ea6e1b..e889b43288 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -514,8 +514,9 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl try { if (file_exists($cachePath) && \is_object($this->container = include $cachePath) - && (!$this->debug || (self::$freshCache[$k = $cachePath.'.'.$this->environment] ?? self::$freshCache[$k] = $cache->isFresh())) + && (!$this->debug || (self::$freshCache[$cachePath] ?? $cache->isFresh())) ) { + self::$freshCache[$cachePath] = true; $this->container->set('kernel', $this); error_reporting($errorLevel); From 986cfc6580740c21947177bb32b9f836869ec975 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 25 Nov 2019 11:39:47 +0100 Subject: [PATCH 29/38] skip test on incompatible PHP versions --- .../Tests/Compiler/CheckTypeDeclarationsPassTest.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php index 40f65f0e31..b012a65625 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php @@ -392,7 +392,10 @@ class CheckTypeDeclarationsPassTest extends TestCase $this->addToAssertionCount(1); } - public function testProcessSuccessWhenPassingDefintionForObjectType() + /** + * @requires PHP 7.2 + */ + public function testProcessSuccessWhenPassingDefinitionForObjectType() { $container = new ContainerBuilder(); From 0f51da6ec7c3e905fcfebd49e99e0b4a12a3a733 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Sat, 23 Nov 2019 16:38:53 +0100 Subject: [PATCH 30/38] [HttpClient] Fix early cleanup of pushed HTTP/2 responses --- .travis.yml | 12 ++ .../Component/HttpClient/CurlHttpClient.php | 30 +++- .../DataCollector/HttpClientDataCollector.php | 25 ++-- .../HttpClient/Response/CurlResponse.php | 8 - .../HttpClient/ScopingHttpClient.php | 10 +- .../HttpClient/Tests/CurlHttpClientTest.php | 140 +++++++++++++++--- .../HttpClient/Tests/Fixtures/tls/server.crt | 20 +++ .../HttpClient/Tests/Fixtures/tls/server.key | 27 ++++ .../HttpClient/TraceableHttpClient.php | 7 +- .../Component/HttpClient/composer.json | 6 +- .../HttpClient/Test/Fixtures/web/index.php | 21 +++ .../HttpClient/Test/HttpClientTestCase.php | 2 - .../HttpClient/Test/TestHttpServer.php | 19 +-- 13 files changed, 254 insertions(+), 73 deletions(-) create mode 100644 src/Symfony/Component/HttpClient/Tests/Fixtures/tls/server.crt create mode 100644 src/Symfony/Component/HttpClient/Tests/Fixtures/tls/server.key diff --git a/.travis.yml b/.travis.yml index 691f8ee2d6..bdf542fe7c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -141,6 +141,12 @@ before_install: (cd php-$MIN_PHP && ./configure --enable-sigchild --enable-pcntl && make -j2) fi + - | + # Install vulcain + wget https://github.com/symfony/binary-utils/releases/download/v0.1/vulcain_0.1.3_Linux_x86_64.tar.gz -O - | tar xz + sudo mv vulcain /usr/local/bin + docker pull php:7.3-alpine + - | # php.ini configuration for PHP in $TRAVIS_PHP_VERSION $php_extra; do @@ -307,8 +313,14 @@ install: PHPUNIT_X="$PHPUNIT_X,legacy" fi + if [[ $PHP = ${MIN_PHP%.*} ]]; then + tfold src/Symfony/Component/HttpClient.h2push docker run -it --rm -v $(pwd):/app -v /usr/local/bin/vulcain:/usr/local/bin/vulcain -w /app php:7.3-alpine ./phpunit src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php --filter testHttp2Push + fi + echo "$COMPONENTS" | parallel --gnu "tfold {} $PHPUNIT_X {}" + tfold src/Symfony/Component/Console.tty $PHPUNIT --group tty + if [[ $PHP = ${MIN_PHP%.*} ]]; then export PHP=$MIN_PHP tfold src/Symfony/Component/Process.sigchild SYMFONY_DEPRECATIONS_HELPER=weak php-$MIN_PHP/sapi/cli/php ./phpunit --colors=always src/Symfony/Component/Process/ diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php index 5e9fe4221b..0f872dfa97 100644 --- a/src/Symfony/Component/HttpClient/CurlHttpClient.php +++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php @@ -23,6 +23,7 @@ use Symfony\Component\HttpClient\Response\ResponseStream; use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\ResponseInterface; use Symfony\Contracts\HttpClient\ResponseStreamInterface; +use Symfony\Contracts\Service\ResetInterface; /** * A performant implementation of the HttpClientInterface contracts based on the curl extension. @@ -32,7 +33,7 @@ use Symfony\Contracts\HttpClient\ResponseStreamInterface; * * @author Nicolas Grekas */ -final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface +final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface, ResetInterface { use HttpClientTrait; use LoggerAwareTrait; @@ -324,9 +325,17 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface return new ResponseStream(CurlResponse::stream($responses, $timeout)); } - public function __destruct() + public function reset() { + if ($this->logger) { + foreach ($this->multi->pushedResponses as $url => $response) { + $this->logger->debug(sprintf('Unused pushed response: "%s"', $url)); + } + } + $this->multi->pushedResponses = []; + $this->multi->dnsCache->evictions = $this->multi->dnsCache->evictions ?: $this->multi->dnsCache->removals; + $this->multi->dnsCache->removals = $this->multi->dnsCache->hostnames = []; if (\is_resource($this->multi->handle)) { if (\defined('CURLMOPT_PUSHFUNCTION')) { @@ -344,6 +353,11 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface } } + public function __destruct() + { + $this->reset(); + } + private static function handlePush($parent, $pushed, array $requestHeaders, CurlClientState $multi, int $maxPendingPushes, ?LoggerInterface $logger): int { $headers = []; @@ -363,12 +377,6 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface $url = $headers[':scheme'][0].'://'.$headers[':authority'][0]; - if ($maxPendingPushes <= \count($multi->pushedResponses)) { - $logger && $logger->debug(sprintf('Rejecting pushed response from "%s" for "%s": the queue is full', $origin, $url)); - - return CURL_PUSH_DENY; - } - // curl before 7.65 doesn't validate the pushed ":authority" header, // but this is a MUST in the HTTP/2 RFC; let's restrict pushes to the original host, // ignoring domains mentioned as alt-name in the certificate for now (same as curl). @@ -378,6 +386,12 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface return CURL_PUSH_DENY; } + if ($maxPendingPushes <= \count($multi->pushedResponses)) { + $fifoUrl = key($multi->pushedResponses); + unset($multi->pushedResponses[$fifoUrl]); + $logger && $logger->debug(sprintf('Evicting oldest pushed response: "%s"', $fifoUrl)); + } + $url .= $headers[':path'][0]; $logger && $logger->debug(sprintf('Queueing pushed response: "%s"', $url)); diff --git a/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php b/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php index b3b0959465..96bbe695b9 100644 --- a/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php +++ b/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php @@ -15,11 +15,12 @@ use Symfony\Component\HttpClient\TraceableHttpClient; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\DataCollector\DataCollector; +use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface; /** * @author Jérémy Romey */ -final class HttpClientDataCollector extends DataCollector +final class HttpClientDataCollector extends DataCollector implements LateDataCollectorInterface { /** * @var TraceableHttpClient[] @@ -38,7 +39,7 @@ final class HttpClientDataCollector extends DataCollector */ public function collect(Request $request, Response $response/*, \Throwable $exception = null*/) { - $this->initData(); + $this->reset(); foreach ($this->clients as $name => $client) { [$errorCount, $traces] = $this->collectOnClient($client); @@ -53,6 +54,13 @@ final class HttpClientDataCollector extends DataCollector } } + public function lateCollect() + { + foreach ($this->clients as $client) { + $client->reset(); + } + } + public function getClients(): array { return $this->data['clients'] ?? []; @@ -68,17 +76,6 @@ final class HttpClientDataCollector extends DataCollector return $this->data['error_count'] ?? 0; } - /** - * {@inheritdoc} - */ - public function reset() - { - $this->initData(); - foreach ($this->clients as $client) { - $client->reset(); - } - } - /** * {@inheritdoc} */ @@ -87,7 +84,7 @@ final class HttpClientDataCollector extends DataCollector return 'http_client'; } - private function initData() + public function reset() { $this->data = [ 'clients' => [], diff --git a/src/Symfony/Component/HttpClient/Response/CurlResponse.php b/src/Symfony/Component/HttpClient/Response/CurlResponse.php index e06f9a58ba..6188587288 100644 --- a/src/Symfony/Component/HttpClient/Response/CurlResponse.php +++ b/src/Symfony/Component/HttpClient/Response/CurlResponse.php @@ -228,15 +228,7 @@ final class CurlResponse implements ResponseInterface } finally { $this->close(); - // Clear local caches when the only remaining handles are about pushed responses if (!$this->multi->openHandles) { - if ($this->logger) { - foreach ($this->multi->pushedResponses as $url => $response) { - $this->logger->debug(sprintf('Unused pushed response: "%s"', $url)); - } - } - - $this->multi->pushedResponses = []; // Schedule DNS cache eviction for the next request $this->multi->dnsCache->evictions = $this->multi->dnsCache->evictions ?: $this->multi->dnsCache->removals; $this->multi->dnsCache->removals = $this->multi->dnsCache->hostnames = []; diff --git a/src/Symfony/Component/HttpClient/ScopingHttpClient.php b/src/Symfony/Component/HttpClient/ScopingHttpClient.php index 3f071720f0..a55d011953 100644 --- a/src/Symfony/Component/HttpClient/ScopingHttpClient.php +++ b/src/Symfony/Component/HttpClient/ScopingHttpClient.php @@ -15,13 +15,14 @@ use Symfony\Component\HttpClient\Exception\InvalidArgumentException; use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\ResponseInterface; use Symfony\Contracts\HttpClient\ResponseStreamInterface; +use Symfony\Contracts\Service\ResetInterface; /** * Auto-configure the default options based on the requested URL. * * @author Anthony Martin */ -class ScopingHttpClient implements HttpClientInterface +class ScopingHttpClient implements HttpClientInterface, ResetInterface { use HttpClientTrait; @@ -90,4 +91,11 @@ class ScopingHttpClient implements HttpClientInterface { return $this->client->stream($responses, $timeout); } + + public function reset() + { + if ($this->client instanceof ResetInterface) { + $this->client->reset(); + } + } } diff --git a/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php index 4fd367fd9d..7aedba1523 100644 --- a/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php +++ b/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php @@ -13,13 +13,23 @@ namespace Symfony\Component\HttpClient\Tests; use Psr\Log\AbstractLogger; use Symfony\Component\HttpClient\CurlHttpClient; +use Symfony\Component\Process\Exception\ProcessFailedException; +use Symfony\Component\Process\Process; use Symfony\Contracts\HttpClient\HttpClientInterface; +/* +Tests for HTTP2 Push need a recent version of both PHP and curl. This docker command should run them: +docker run -it --rm -v $(pwd):/app -v /path/to/vulcain:/usr/local/bin/vulcain -w /app php:7.3-alpine ./phpunit src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php --filter testHttp2Push +The vulcain binary can be found at https://github.com/symfony/binary-utils/releases/download/v0.1/vulcain_0.1.3_Linux_x86_64.tar.gz - see https://github.com/dunglas/vulcain for source +*/ + /** * @requires extension curl */ class CurlHttpClientTest extends HttpClientTestCase { + private static $vulcainStarted = false; + protected function getHttpClient(string $testCase): HttpClientInterface { return new CurlHttpClient(); @@ -28,7 +38,81 @@ class CurlHttpClientTest extends HttpClientTestCase /** * @requires PHP 7.2.17 */ - public function testHttp2Push() + public function testHttp2PushVulcain() + { + $client = $this->getVulcainClient(); + $logger = new TestLogger(); + $client->setLogger($logger); + + $responseAsArray = $client->request('GET', 'https://127.0.0.1:3000/json', [ + 'headers' => [ + 'Preload' => '/documents/*/id', + ], + ])->toArray(); + + foreach ($responseAsArray['documents'] as $document) { + $client->request('GET', 'https://127.0.0.1:3000'.$document['id'])->toArray(); + } + + $client->reset(); + + $expected = [ + 'Request: "GET https://127.0.0.1:3000/json"', + 'Queueing pushed response: "https://127.0.0.1:3000/json/1"', + 'Queueing pushed response: "https://127.0.0.1:3000/json/2"', + 'Queueing pushed response: "https://127.0.0.1:3000/json/3"', + 'Response: "200 https://127.0.0.1:3000/json"', + 'Accepting pushed response: "GET https://127.0.0.1:3000/json/1"', + 'Response: "200 https://127.0.0.1:3000/json/1"', + 'Accepting pushed response: "GET https://127.0.0.1:3000/json/2"', + 'Response: "200 https://127.0.0.1:3000/json/2"', + 'Accepting pushed response: "GET https://127.0.0.1:3000/json/3"', + 'Response: "200 https://127.0.0.1:3000/json/3"', + ]; + $this->assertSame($expected, $logger->logs); + } + + /** + * @requires PHP 7.2.17 + */ + public function testHttp2PushVulcainWithUnusedResponse() + { + $client = $this->getVulcainClient(); + $logger = new TestLogger(); + $client->setLogger($logger); + + $responseAsArray = $client->request('GET', 'https://127.0.0.1:3000/json', [ + 'headers' => [ + 'Preload' => '/documents/*/id', + ], + ])->toArray(); + + $i = 0; + foreach ($responseAsArray['documents'] as $document) { + $client->request('GET', 'https://127.0.0.1:3000'.$document['id'])->toArray(); + if (++$i >= 2) { + break; + } + } + + $client->reset(); + + $expected = [ + 'Request: "GET https://127.0.0.1:3000/json"', + 'Queueing pushed response: "https://127.0.0.1:3000/json/1"', + 'Queueing pushed response: "https://127.0.0.1:3000/json/2"', + 'Queueing pushed response: "https://127.0.0.1:3000/json/3"', + 'Response: "200 https://127.0.0.1:3000/json"', + 'Accepting pushed response: "GET https://127.0.0.1:3000/json/1"', + 'Response: "200 https://127.0.0.1:3000/json/1"', + 'Accepting pushed response: "GET https://127.0.0.1:3000/json/2"', + 'Response: "200 https://127.0.0.1:3000/json/2"', + 'Unused pushed response: "https://127.0.0.1:3000/json/3"', + ]; + $this->assertSame($expected, $logger->logs); + } + + private function getVulcainClient(): CurlHttpClient { if (\PHP_VERSION_ID >= 70300 && \PHP_VERSION_ID < 70304) { $this->markTestSkipped('PHP 7.3.0 to 7.3.3 don\'t support HTTP/2 PUSH'); @@ -38,32 +122,44 @@ class CurlHttpClientTest extends HttpClientTestCase $this->markTestSkipped('curl <7.61 is used or it is not compiled with support for HTTP/2 PUSH'); } - $logger = new class() extends AbstractLogger { - public $logs = []; + $client = new CurlHttpClient(['verify_peer' => false, 'verify_host' => false]); - public function log($level, $message, array $context = []): void - { - $this->logs[] = $message; - } - }; + if (static::$vulcainStarted) { + return $client; + } - $client = new CurlHttpClient([], 6, 2); - $client->setLogger($logger); + if (200 !== $client->request('GET', 'http://127.0.0.1:8057/json')->getStatusCode()) { + $this->markTestSkipped('symfony/http-client-contracts >= 2.0.1 required'); + } - $index = $client->request('GET', 'https://http2.akamai.com/'); - $index->getContent(); + $process = new Process(['vulcain'], null, [ + 'DEBUG' => 1, + 'UPSTREAM' => 'http://127.0.0.1:8057', + 'ADDR' => ':3000', + 'KEY_FILE' => __DIR__.'/Fixtures/tls/server.key', + 'CERT_FILE' => __DIR__.'/Fixtures/tls/server.crt', + ]); + $process->start(); - $css = $client->request('GET', 'https://http2.akamai.com/resources/push.css'); + register_shutdown_function([$process, 'stop']); + sleep('\\' === \DIRECTORY_SEPARATOR ? 10 : 1); - $css->getHeaders(); + if (!$process->isRunning()) { + throw new ProcessFailedException($process); + } - $expected = [ - 'Request: "GET https://http2.akamai.com/"', - 'Queueing pushed response: "https://http2.akamai.com/resources/push.css"', - 'Response: "200 https://http2.akamai.com/"', - 'Accepting pushed response: "GET https://http2.akamai.com/resources/push.css"', - 'Response: "200 https://http2.akamai.com/resources/push.css"', - ]; - $this->assertSame($expected, $logger->logs); + static::$vulcainStarted = true; + + return $client; + } +} + +class TestLogger extends AbstractLogger +{ + public $logs = []; + + public function log($level, $message, array $context = []): void + { + $this->logs[] = $message; } } diff --git a/src/Symfony/Component/HttpClient/Tests/Fixtures/tls/server.crt b/src/Symfony/Component/HttpClient/Tests/Fixtures/tls/server.crt new file mode 100644 index 0000000000..3903667223 --- /dev/null +++ b/src/Symfony/Component/HttpClient/Tests/Fixtures/tls/server.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDPjCCAiYCCQDpVvfmCZt2GzANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJV +UzEUMBIGA1UEBwwLR290aGFtIENpdHkxEjAQBgNVBAMMCWxvY2FsaG9zdDEoMCYG +CSqGSIb3DQEJARYZZHVuZ2xhcyttZXJjdXJlQGdtYWlsLmNvbTAeFw0xOTAxMjMx +NTUzMzlaFw0yOTAxMjAxNTUzMzlaMGExCzAJBgNVBAYTAlVTMRQwEgYDVQQHDAtH +b3RoYW0gQ2l0eTESMBAGA1UEAwwJbG9jYWxob3N0MSgwJgYJKoZIhvcNAQkBFhlk +dW5nbGFzK21lcmN1cmVAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAuKnXkBSJwOwkKfR58wP/yLYW9QFX2THoqN8iffangRmZwc5KLE6F +1S8jYMv3JGiJ95Ij3MezAfuBCdgPqqP8JrR1XwjR1RFZMOL/4U9R9OuMVng04PLw +L6TzKoEtZuExHUWFP0+5AYblgno2hoN/HVuox8m6zQrBNcbhTgDIjP5Hn491d9od +MtS3OxksDLr1UIOUGUWF7MQMN7lsN7rgT5qxoCkcAGAB4GPOA23HMt2zt4afDiI7 +lAmuv8MKkTmBCcFe+q+U7o6wMxkjGstzAWRibtwzR4ejPwdO7se23MXCWGPvF16Z +tu1ip+e+waRus9o5UnyGaVPFAw8iCTC/KwIDAQABMA0GCSqGSIb3DQEBCwUAA4IB +AQB42AW7E57yOky8GpsKLoa9u7okwvvg8CQJ117X8a2MElBGnmMd9tjLa/pXAx2I +bN7jSTSadXiPNYCx4ueiJa4Dwy+C8YkwUbhRf3+mc7Chnz0SXouTjh7OUeeA06jS +W2VAR2pKB0pdJtAkXxIy21Juu8KF5uZqVq1oimgKw2lRUIMdKaqsrVwESk6u5Ojj +3DS40q9DzFnwKGCuZpspvMdWYLscotzLrCbnHp+guWDigEHS3CKzKbNo327nVg6X +7UjqqtPZ2mCsnUx3QTDJsr3gcSqhzmB+Q6I/0Q2Nx/aMmbsNegu+LC3GjFtL59Bv +B8pB/MxID0j47SwPKQghZvb3 +-----END CERTIFICATE----- diff --git a/src/Symfony/Component/HttpClient/Tests/Fixtures/tls/server.key b/src/Symfony/Component/HttpClient/Tests/Fixtures/tls/server.key new file mode 100644 index 0000000000..8c278f843d --- /dev/null +++ b/src/Symfony/Component/HttpClient/Tests/Fixtures/tls/server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAuKnXkBSJwOwkKfR58wP/yLYW9QFX2THoqN8iffangRmZwc5K +LE6F1S8jYMv3JGiJ95Ij3MezAfuBCdgPqqP8JrR1XwjR1RFZMOL/4U9R9OuMVng0 +4PLwL6TzKoEtZuExHUWFP0+5AYblgno2hoN/HVuox8m6zQrBNcbhTgDIjP5Hn491 +d9odMtS3OxksDLr1UIOUGUWF7MQMN7lsN7rgT5qxoCkcAGAB4GPOA23HMt2zt4af +DiI7lAmuv8MKkTmBCcFe+q+U7o6wMxkjGstzAWRibtwzR4ejPwdO7se23MXCWGPv +F16Ztu1ip+e+waRus9o5UnyGaVPFAw8iCTC/KwIDAQABAoIBAQCczVNGe7oRADMh +EP/wM4ghhUTvHAndWrzFkFs4fJX1UKi34ZQoFTEdOZ6f1fHwj3f/qa8cDNJar5X9 +puJ+siotL3Suks2iT83dbhN63SCpiM2sqvuzu3Xp7vWwNOo5fqR2x46CmQ5uVn5S +EbZ09/mbEza5FvmwnB49rLepxY6F8P+vK5ZnCZYS2SHpOxv3U9wG8gmcHRI9ejbC +X9rwuu3oT23bfbJ0tn6Qh8O3R1kXZUUXqnxsn554cZZrXg5+ygbt4HfDVWMLpqy/ +5wG0FCpU8QvjF4L8qErP7TZRrWGFtti1RtACbu9LrWvO/74v54td5V28U6kqlDJR +ff4Mi4whAoGBAOBzReQIxGwoYApPyhF+ohvF39JEEXYfkzk94t6hbgyBFBFvqdFY +shT59im2P9LyDvTd5DnCIo52Sj7vM9H80tRjAA0A8okGOczk31ABbH8aZ2orU/0G +EJe4PV4r3bpLO6DKTYsicgRpXI3aHHLvYFXOVNrQKfrKCQ+GFMVuhDdRAoGBANKe +3Dn3XOq7EW42GZey1xUxrfQRJp491KXHvjYt7z7zSiUzqN+mqIqz6ngCjJWbyQsl +Ud9N9U+4rNfYYLHQ0resjxGQRtmooOHlLhT6pEplXDgQb2SmCg2u22SKkkXA7zOV +OFbNryXgkYThsA6ih8LiKM8aFn7zttRSEeTpfye7AoGBALhIzRyiuiuXpuswgdeF +YrJs8A1jB/c1i5qXHlvurT2lCYYbaZHSQj0I0r2CvrqDNhaEzStDIz5XDzTHD4Qd +EjmBo3wJyBkLPI/nZxb4ZE2jrz8znf0EasE3a2OTnrSjqqylDa/sMzM+EtkBORSB +SFaLV45lFeKs2W2eiBVmXTZRAoGAJoA7qaz6Iz6G9SqWixB6GLm4HsFz2cFbueJF +dwn2jf9TMnG7EQcaECDLX5y3rjGIEq2DxdouWaBcmChJpLeTjVfR31gMW4Vjw2dt +gRBAMAlPTkBS3Ictl0q7eCmMi4u1Liy828FFnxrp/uxyjnpPbuSAqTsPma1bYnyO +INY+FDkCgYAe9e39/vXe7Un3ysjqDUW+0OMM+kg4ulhiopzKY+QbHiSWmUUDtvcN +asqrYiX1d59e2ZNiqrlBn86I8549St81bWSrRMNf7R+WVb79RApsABeUaEoyo3lq +0UgOBM8Nt558kaja/YfJf/jwNC1DPuu5x5t38ZcqAkqrZ/HEPkFdGQ== +-----END RSA PRIVATE KEY----- diff --git a/src/Symfony/Component/HttpClient/TraceableHttpClient.php b/src/Symfony/Component/HttpClient/TraceableHttpClient.php index 4acbc8ee42..d60d0849cd 100644 --- a/src/Symfony/Component/HttpClient/TraceableHttpClient.php +++ b/src/Symfony/Component/HttpClient/TraceableHttpClient.php @@ -14,11 +14,12 @@ namespace Symfony\Component\HttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\ResponseInterface; use Symfony\Contracts\HttpClient\ResponseStreamInterface; +use Symfony\Contracts\Service\ResetInterface; /** * @author Jérémy Romey */ -final class TraceableHttpClient implements HttpClientInterface +final class TraceableHttpClient implements HttpClientInterface, ResetInterface { private $client; private $tracedRequests = []; @@ -68,6 +69,10 @@ final class TraceableHttpClient implements HttpClientInterface public function reset() { + if ($this->client instanceof ResetInterface) { + $this->client->reset(); + } + $this->tracedRequests = []; } } diff --git a/src/Symfony/Component/HttpClient/composer.json b/src/Symfony/Component/HttpClient/composer.json index e734b21554..be158f3296 100644 --- a/src/Symfony/Component/HttpClient/composer.json +++ b/src/Symfony/Component/HttpClient/composer.json @@ -24,7 +24,8 @@ "php": "^7.1.3", "psr/log": "^1.0", "symfony/http-client-contracts": "^1.1.8|^2", - "symfony/polyfill-php73": "^1.11" + "symfony/polyfill-php73": "^1.11", + "symfony/service-contracts": "^1.0|^2" }, "require-dev": { "guzzlehttp/promises": "^1.3.1", @@ -33,8 +34,7 @@ "psr/http-client": "^1.0", "symfony/dependency-injection": "^4.3|^5.0", "symfony/http-kernel": "^4.4", - "symfony/process": "^4.2|^5.0", - "symfony/service-contracts": "^1.0|^2" + "symfony/process": "^4.2|^5.0" }, "conflict": { "symfony/http-kernel": "<4.4" diff --git a/src/Symfony/Contracts/HttpClient/Test/Fixtures/web/index.php b/src/Symfony/Contracts/HttpClient/Test/Fixtures/web/index.php index d3c4f0f1db..96486ca316 100644 --- a/src/Symfony/Contracts/HttpClient/Test/Fixtures/web/index.php +++ b/src/Symfony/Contracts/HttpClient/Test/Fixtures/web/index.php @@ -155,6 +155,27 @@ switch ($vars['REQUEST_URI']) { usleep(500); } exit; + + case '/json': + header("Content-Type: application/json"); + echo json_encode([ + 'documents' => [ + ['id' => '/json/1'], + ['id' => '/json/2'], + ['id' => '/json/3'], + ], + ]); + exit; + + case '/json/1': + case '/json/2': + case '/json/3': + header("Content-Type: application/json"); + echo json_encode([ + 'title' => $vars['REQUEST_URI'], + ]); + + exit; } header('Content-Type: application/json', true); diff --git a/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php b/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php index 4badb4c358..aad712e3e8 100644 --- a/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php +++ b/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php @@ -24,8 +24,6 @@ use Symfony\Contracts\HttpClient\HttpClientInterface; */ abstract class HttpClientTestCase extends TestCase { - private static $server; - public static function setUpBeforeClass(): void { TestHttpServer::start(); diff --git a/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php b/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php index 8e7a469c42..0adb1a52a3 100644 --- a/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php +++ b/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php @@ -19,31 +19,22 @@ use Symfony\Component\Process\Process; */ class TestHttpServer { - private static $server; + private static $started; public static function start() { - if (null !== self::$server) { + if (self::$started) { return; } $finder = new PhpExecutableFinder(); $process = new Process(array_merge([$finder->find(false)], $finder->findArguments(), ['-dopcache.enable=0', '-dvariables_order=EGPCS', '-S', '127.0.0.1:8057'])); $process->setWorkingDirectory(__DIR__.'/Fixtures/web'); - $process->setTimeout(300); $process->start(); - self::$server = new class() { - public $process; - - public function __destruct() - { - $this->process->stop(); - } - }; - - self::$server->process = $process; - + register_shutdown_function([$process, 'stop']); sleep('\\' === \DIRECTORY_SEPARATOR ? 10 : 1); + + self::$started = true; } } From 78ff806b77c1cb8ac07b5adaff58548881e57453 Mon Sep 17 00:00:00 2001 From: Lynn Date: Mon, 25 Nov 2019 15:00:05 +0100 Subject: [PATCH 31/38] has_roles should be is_granted in upgrade files --- UPGRADE-4.4.md | 2 +- UPGRADE-5.0.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/UPGRADE-4.4.md b/UPGRADE-4.4.md index 2dffdb1984..4bb7edc5d9 100644 --- a/UPGRADE-4.4.md +++ b/UPGRADE-4.4.md @@ -230,7 +230,7 @@ Security **After** ```php - if ($this->authorizationChecker->isGranted(new Expression("has_role('ROLE_USER') or has_role('ROLE_ADMIN')"))) {} + if ($this->authorizationChecker->isGranted(new Expression("is_granted('ROLE_USER') or is_granted('ROLE_ADMIN')"))) {} // or: if ($this->authorizationChecker->isGranted('ROLE_USER') diff --git a/UPGRADE-5.0.md b/UPGRADE-5.0.md index 9dbd28e2a3..8a0b3ebe27 100644 --- a/UPGRADE-5.0.md +++ b/UPGRADE-5.0.md @@ -410,7 +410,7 @@ Security **After** ```php - if ($this->authorizationChecker->isGranted(new Expression("has_role('ROLE_USER') or has_role('ROLE_ADMIN')"))) {} + if ($this->authorizationChecker->isGranted(new Expression("is_granted('ROLE_USER') or is_granted('ROLE_ADMIN')"))) {} // or: if ($this->authorizationChecker->isGranted('ROLE_USER') @@ -473,6 +473,7 @@ Security * Classes implementing the `TokenInterface` must implement the two new methods `__serialize` and `__unserialize` * Implementations of `Guard\AuthenticatorInterface::checkCredentials()` must return a boolean value now. Please explicitly return `false` to indicate invalid credentials. + * Removed the `has_role()` function from security expressions, use `is_granted()` instead. SecurityBundle -------------- From fa3f901cf87d2b97297cd625a039f2ba0a1530ff Mon Sep 17 00:00:00 2001 From: Hugo Monteiro Date: Mon, 25 Nov 2019 14:55:17 +0000 Subject: [PATCH 32/38] mailer: mailchimp bridge is throwing undefined index _id when setting message id in mandrill http transport --- .../Mailer/Bridge/Mailchimp/Transport/MandrillHttpTransport.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillHttpTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillHttpTransport.php index 2c90472fc6..17f7a7fcf3 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillHttpTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillHttpTransport.php @@ -60,7 +60,7 @@ class MandrillHttpTransport extends AbstractHttpTransport throw new HttpTransportException(sprintf('Unable to send an email (code %s).', $result['code']), $response); } - $message->setMessageId($result['_id']); + $message->setMessageId($result[0]['_id']); return $response; } From 7524ac2866fee55fe563b3252450dede03d9f468 Mon Sep 17 00:00:00 2001 From: Hugo Hamon Date: Mon, 25 Nov 2019 17:36:22 +0100 Subject: [PATCH 33/38] [Filesystem] [Serializer] fixes English grammar typo --- src/Symfony/Component/Filesystem/Tests/FilesystemTest.php | 2 +- .../Component/Serializer/Mapping/Factory/ClassResolverTrait.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php index a5b7f6dceb..e9e7784a3a 100644 --- a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php +++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php @@ -801,7 +801,7 @@ class FilesystemTest extends FilesystemTestCase $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; $link = $this->workspace.\DIRECTORY_SEPARATOR.'link'; - // $file does not exists right now: creating "broken" links is a wanted feature + // $file does not exist right now: creating "broken" links is a wanted feature $this->filesystem->symlink($file, $link); $this->assertTrue(is_link($link)); diff --git a/src/Symfony/Component/Serializer/Mapping/Factory/ClassResolverTrait.php b/src/Symfony/Component/Serializer/Mapping/Factory/ClassResolverTrait.php index 73c02a647c..5c58476383 100644 --- a/src/Symfony/Component/Serializer/Mapping/Factory/ClassResolverTrait.php +++ b/src/Symfony/Component/Serializer/Mapping/Factory/ClassResolverTrait.php @@ -29,7 +29,7 @@ trait ClassResolverTrait * * @return string * - * @throws InvalidArgumentException If the class does not exists + * @throws InvalidArgumentException If the class does not exist */ private function getClass($value) { From 492e1b5afd823da24e8f05d139cc2cd2d5c7bc9c Mon Sep 17 00:00:00 2001 From: Hugo Hamon Date: Mon, 25 Nov 2019 17:25:29 +0100 Subject: [PATCH 34/38] [Intl] [Workflow] fixes English grammar typos --- src/Symfony/Component/Intl/Countries.php | 2 +- src/Symfony/Component/Intl/Currencies.php | 6 +++--- src/Symfony/Component/Intl/Languages.php | 2 +- src/Symfony/Component/Intl/Locales.php | 2 +- src/Symfony/Component/Intl/Scripts.php | 2 +- .../Component/Workflow/MarkingStore/MethodMarkingStore.php | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Symfony/Component/Intl/Countries.php b/src/Symfony/Component/Intl/Countries.php index 07812f5131..66489177a4 100644 --- a/src/Symfony/Component/Intl/Countries.php +++ b/src/Symfony/Component/Intl/Countries.php @@ -55,7 +55,7 @@ final class Countries extends ResourceBundle /** * Gets the country name from alpha2 code. * - * @throws MissingResourceException if the country code does not exists + * @throws MissingResourceException if the country code does not exist */ public static function getName(string $country, string $displayLocale = null): string { diff --git a/src/Symfony/Component/Intl/Currencies.php b/src/Symfony/Component/Intl/Currencies.php index 7459d633cb..c155c8f09f 100644 --- a/src/Symfony/Component/Intl/Currencies.php +++ b/src/Symfony/Component/Intl/Currencies.php @@ -46,7 +46,7 @@ final class Currencies extends ResourceBundle } /** - * @throws MissingResourceException if the currency code does not exists + * @throws MissingResourceException if the currency code does not exist */ public static function getName(string $currency, string $displayLocale = null): string { @@ -78,7 +78,7 @@ final class Currencies extends ResourceBundle } /** - * @throws MissingResourceException if the currency code does not exists + * @throws MissingResourceException if the currency code does not exist */ public static function getSymbol(string $currency, string $displayLocale = null): string { @@ -115,7 +115,7 @@ final class Currencies extends ResourceBundle } /** - * @throws MissingResourceException if the numeric code does not exists + * @throws MissingResourceException if the numeric code does not exist */ public static function forNumericCode(int $numericCode): array { diff --git a/src/Symfony/Component/Intl/Languages.php b/src/Symfony/Component/Intl/Languages.php index a70a7e8b9c..9776a8501e 100644 --- a/src/Symfony/Component/Intl/Languages.php +++ b/src/Symfony/Component/Intl/Languages.php @@ -52,7 +52,7 @@ final class Languages extends ResourceBundle /** * Gets the language name from alpha2 code. * - * @throws MissingResourceException if the language code does not exists + * @throws MissingResourceException if the language code does not exist */ public static function getName(string $language, string $displayLocale = null): string { diff --git a/src/Symfony/Component/Intl/Locales.php b/src/Symfony/Component/Intl/Locales.php index aee16beb16..1b2d0d2adf 100644 --- a/src/Symfony/Component/Intl/Locales.php +++ b/src/Symfony/Component/Intl/Locales.php @@ -49,7 +49,7 @@ final class Locales extends ResourceBundle } /** - * @throws MissingResourceException if the locale does not exists + * @throws MissingResourceException if the locale does not exist */ public static function getName(string $locale, string $displayLocale = null): string { diff --git a/src/Symfony/Component/Intl/Scripts.php b/src/Symfony/Component/Intl/Scripts.php index 943ef8b469..bb29f0095b 100644 --- a/src/Symfony/Component/Intl/Scripts.php +++ b/src/Symfony/Component/Intl/Scripts.php @@ -41,7 +41,7 @@ final class Scripts extends ResourceBundle } /** - * @throws MissingResourceException if the script code does not exists + * @throws MissingResourceException if the script code does not exist */ public static function getName(string $script, string $displayLocale = null): string { diff --git a/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php b/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php index 3835765265..e5ba4deae1 100644 --- a/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php +++ b/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php @@ -51,7 +51,7 @@ final class MethodMarkingStore implements MarkingStoreInterface $method = 'get'.ucfirst($this->property); if (!method_exists($subject, $method)) { - throw new LogicException(sprintf('The method "%s::%s()" does not exists.', \get_class($subject), $method)); + throw new LogicException(sprintf('The method "%s::%s()" does not exist.', \get_class($subject), $method)); } $marking = $subject->{$method}(); @@ -81,7 +81,7 @@ final class MethodMarkingStore implements MarkingStoreInterface $method = 'set'.ucfirst($this->property); if (!method_exists($subject, $method)) { - throw new LogicException(sprintf('The method "%s::%s()" does not exists.', \get_class($subject), $method)); + throw new LogicException(sprintf('The method "%s::%s()" does not exist.', \get_class($subject), $method)); } $subject->{$method}($marking, $context); From 22a7f40cb88c600d1b5e35549ce82523648ac4f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Mon, 25 Nov 2019 23:12:50 +0100 Subject: [PATCH 35/38] [FWBundle] Remove unused parameter --- .../DependencyInjection/FrameworkExtension.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 2ad1566a25..f29c72e95a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -283,7 +283,7 @@ class FrameworkExtension extends Extension } if ($this->messengerConfigEnabled = $this->isConfigEnabled($container, $config['messenger'])) { - $this->registerMessengerConfiguration($config['messenger'], $container, $loader, $config['serializer'], $config['validation']); + $this->registerMessengerConfiguration($config['messenger'], $container, $loader, $config['validation']); } else { $container->removeDefinition('console.command.messenger_consume_messages'); $container->removeDefinition('console.command.messenger_debug'); @@ -1639,7 +1639,7 @@ class FrameworkExtension extends Extension } } - private function registerMessengerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader, array $serializerConfig, array $validationConfig) + private function registerMessengerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader, array $validationConfig) { if (!interface_exists(MessageBusInterface::class)) { throw new LogicException('Messenger support cannot be enabled as the Messenger component is not installed. Try running "composer require symfony/messenger".'); From 3c796e120c170e8b3f4d7e97722490d451d0224e Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Tue, 26 Nov 2019 09:17:35 +0100 Subject: [PATCH 36/38] [Routing] Continue supporting single colon in object route loaders --- .../Component/Routing/Loader/ObjectLoader.php | 7 +++++- .../Routing/Loader/ObjectRouteLoader.php | 24 ------------------- .../Tests/Loader/ObjectRouteLoaderTest.php | 2 +- 3 files changed, 7 insertions(+), 26 deletions(-) diff --git a/src/Symfony/Component/Routing/Loader/ObjectLoader.php b/src/Symfony/Component/Routing/Loader/ObjectLoader.php index c391ad6648..16dd6c9a36 100644 --- a/src/Symfony/Component/Routing/Loader/ObjectLoader.php +++ b/src/Symfony/Component/Routing/Loader/ObjectLoader.php @@ -42,10 +42,15 @@ abstract class ObjectLoader extends Loader */ public function load($resource, $type = null) { - if (!preg_match('/^[^\:]+(?:::(?:[^\:]+))?$/', $resource)) { + if (!preg_match('/^[^\:]+(?:::?(?:[^\:]+))?$/', $resource)) { throw new \InvalidArgumentException(sprintf('Invalid resource "%s" passed to the %s route loader: use the format "object_id::method" or "object_id" if your object class has an "__invoke" method.', $resource, \is_string($type) ? '"'.$type.'"' : 'object')); } + if (1 === substr_count($resource, ':')) { + $resource = str_replace(':', '::', $resource); + @trigger_error(sprintf('Referencing object route loaders with a single colon is deprecated since Symfony 4.1. Use %s instead.', $resource), E_USER_DEPRECATED); + } + $parts = explode('::', $resource); $method = $parts[1] ?? '__invoke'; diff --git a/src/Symfony/Component/Routing/Loader/ObjectRouteLoader.php b/src/Symfony/Component/Routing/Loader/ObjectRouteLoader.php index 2bed560322..39833d9ce6 100644 --- a/src/Symfony/Component/Routing/Loader/ObjectRouteLoader.php +++ b/src/Symfony/Component/Routing/Loader/ObjectRouteLoader.php @@ -11,8 +11,6 @@ namespace Symfony\Component\Routing\Loader; -use Symfony\Component\Routing\RouteCollection; - @trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', ObjectRouteLoader::class, ObjectLoader::class), E_USER_DEPRECATED); /** @@ -36,28 +34,6 @@ abstract class ObjectRouteLoader extends ObjectLoader */ abstract protected function getServiceObject($id); - /** - * Calls the service that will load the routes. - * - * @param string $resource Some value that will resolve to a callable - * @param string|null $type The resource type - * - * @return RouteCollection - */ - public function load($resource, $type = null) - { - if (!preg_match('/^[^\:]+(?:::?(?:[^\:]+))?$/', $resource)) { - throw new \InvalidArgumentException(sprintf('Invalid resource "%s" passed to the "service" route loader: use the format "service::method" or "service" if your service has an "__invoke" method.', $resource)); - } - - if (1 === substr_count($resource, ':')) { - $resource = str_replace(':', '::', $resource); - @trigger_error(sprintf('Referencing service route loaders with a single colon is deprecated since Symfony 4.1. Use %s instead.', $resource), E_USER_DEPRECATED); - } - - return parent::load($resource, $type); - } - /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Routing/Tests/Loader/ObjectRouteLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/ObjectRouteLoaderTest.php index cc0aa5d507..3436d60693 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/ObjectRouteLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/ObjectRouteLoaderTest.php @@ -22,7 +22,7 @@ use Symfony\Component\Routing\Tests\Fixtures\TestObjectRouteLoader; class ObjectRouteLoaderTest extends TestCase { /** - * @expectedDeprecation Referencing service route loaders with a single colon is deprecated since Symfony 4.1. Use my_route_provider_service::loadRoutes instead. + * @expectedDeprecation Referencing object route loaders with a single colon is deprecated since Symfony 4.1. Use my_route_provider_service::loadRoutes instead. */ public function testLoadCallsServiceAndReturnsCollectionWithLegacyNotation() { From 6f8dbf1a5f04e0baf5892f6e7d76121e4f3a1cc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rokas=20Mikalk=C4=97nas?= Date: Mon, 25 Nov 2019 19:31:29 +0200 Subject: [PATCH 37/38] [HttpKernel] Ability to define multiple kernel.reset tags --- .../ResettableServicePass.php | 17 +++++++++++------ .../DependencyInjection/ServicesResetter.php | 4 +++- .../ResettableServicePassTest.php | 13 ++++++++++--- .../ServicesResetterTest.php | 15 +++++++++++---- .../Tests/Fixtures/MultiResettableService.php | 19 +++++++++++++++++++ 5 files changed, 54 insertions(+), 14 deletions(-) create mode 100644 src/Symfony/Component/HttpKernel/Tests/Fixtures/MultiResettableService.php diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/ResettableServicePass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/ResettableServicePass.php index c1199f639e..b5e46106a7 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/ResettableServicePass.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/ResettableServicePass.php @@ -43,16 +43,21 @@ class ResettableServicePass implements CompilerPassInterface foreach ($container->findTaggedServiceIds($this->tagName, true) as $id => $tags) { $services[$id] = new Reference($id, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE); - $attributes = $tags[0]; - if (!isset($attributes['method'])) { - throw new RuntimeException(sprintf('Tag %s requires the "method" attribute to be set.', $this->tagName)); + foreach ($tags as $attributes) { + if (!isset($attributes['method'])) { + throw new RuntimeException(sprintf('Tag "%s" requires the "method" attribute to be set.', $this->tagName)); + } + + if (!isset($methods[$id])) { + $methods[$id] = []; + } + + $methods[$id][] = $attributes['method']; } - - $methods[$id] = $attributes['method']; } - if (empty($services)) { + if (!$services) { $container->removeAlias('services_resetter'); $container->removeDefinition('services_resetter'); diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/ServicesResetter.php b/src/Symfony/Component/HttpKernel/DependencyInjection/ServicesResetter.php index 734fadbd74..d9e0028ce1 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/ServicesResetter.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/ServicesResetter.php @@ -35,7 +35,9 @@ class ServicesResetter implements ResetInterface public function reset() { foreach ($this->resettableServices as $id => $service) { - $service->{$this->resetMethods[$id]}(); + foreach ((array) $this->resetMethods[$id] as $resetMethod) { + $service->$resetMethod(); + } } } } diff --git a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ResettableServicePassTest.php b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ResettableServicePassTest.php index d3c6869320..9dbc2b08a4 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ResettableServicePassTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ResettableServicePassTest.php @@ -10,6 +10,7 @@ use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpKernel\DependencyInjection\ResettableServicePass; use Symfony\Component\HttpKernel\DependencyInjection\ServicesResetter; use Symfony\Component\HttpKernel\Tests\Fixtures\ClearableService; +use Symfony\Component\HttpKernel\Tests\Fixtures\MultiResettableService; use Symfony\Component\HttpKernel\Tests\Fixtures\ResettableService; class ResettableServicePassTest extends TestCase @@ -23,6 +24,10 @@ class ResettableServicePassTest extends TestCase $container->register('two', ClearableService::class) ->setPublic(true) ->addTag('kernel.reset', ['method' => 'clear']); + $container->register('three', MultiResettableService::class) + ->setPublic(true) + ->addTag('kernel.reset', ['method' => 'resetFirst']) + ->addTag('kernel.reset', ['method' => 'resetSecond']); $container->register('services_resetter', ServicesResetter::class) ->setPublic(true) @@ -38,10 +43,12 @@ class ResettableServicePassTest extends TestCase new IteratorArgument([ 'one' => new Reference('one', ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE), 'two' => new Reference('two', ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE), + 'three' => new Reference('three', ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE), ]), [ - 'one' => 'reset', - 'two' => 'clear', + 'one' => ['reset'], + 'two' => ['clear'], + 'three' => ['resetFirst', 'resetSecond'], ], ], $definition->getArguments() @@ -51,7 +58,7 @@ class ResettableServicePassTest extends TestCase public function testMissingMethod() { $this->expectException('Symfony\Component\DependencyInjection\Exception\RuntimeException'); - $this->expectExceptionMessage('Tag kernel.reset requires the "method" attribute to be set.'); + $this->expectExceptionMessage('Tag "kernel.reset" requires the "method" attribute to be set.'); $container = new ContainerBuilder(); $container->register(ResettableService::class) ->addTag('kernel.reset'); diff --git a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ServicesResetterTest.php b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ServicesResetterTest.php index 5be6026c90..604d2b0d13 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ServicesResetterTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ServicesResetterTest.php @@ -14,6 +14,7 @@ namespace Symfony\Component\HttpKernel\Tests\DependencyInjection; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpKernel\DependencyInjection\ServicesResetter; use Symfony\Component\HttpKernel\Tests\Fixtures\ClearableService; +use Symfony\Component\HttpKernel\Tests\Fixtures\MultiResettableService; use Symfony\Component\HttpKernel\Tests\Fixtures\ResettableService; class ServicesResetterTest extends TestCase @@ -22,6 +23,8 @@ class ServicesResetterTest extends TestCase { ResettableService::$counter = 0; ClearableService::$counter = 0; + MultiResettableService::$resetFirstCounter = 0; + MultiResettableService::$resetSecondCounter = 0; } public function testResetServices() @@ -29,14 +32,18 @@ class ServicesResetterTest extends TestCase $resetter = new ServicesResetter(new \ArrayIterator([ 'id1' => new ResettableService(), 'id2' => new ClearableService(), + 'id3' => new MultiResettableService(), ]), [ - 'id1' => 'reset', - 'id2' => 'clear', + 'id1' => ['reset'], + 'id2' => ['clear'], + 'id3' => ['resetFirst', 'resetSecond'], ]); $resetter->reset(); - $this->assertEquals(1, ResettableService::$counter); - $this->assertEquals(1, ClearableService::$counter); + $this->assertSame(1, ResettableService::$counter); + $this->assertSame(1, ClearableService::$counter); + $this->assertSame(1, MultiResettableService::$resetFirstCounter); + $this->assertSame(1, MultiResettableService::$resetSecondCounter); } } diff --git a/src/Symfony/Component/HttpKernel/Tests/Fixtures/MultiResettableService.php b/src/Symfony/Component/HttpKernel/Tests/Fixtures/MultiResettableService.php new file mode 100644 index 0000000000..4930fd6a30 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Tests/Fixtures/MultiResettableService.php @@ -0,0 +1,19 @@ + Date: Tue, 26 Nov 2019 10:38:45 +0100 Subject: [PATCH 38/38] reset the kernel cache after each test --- src/Symfony/Component/HttpKernel/Tests/KernelTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php index 1f6811639b..deac82b72d 100644 --- a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php @@ -30,7 +30,7 @@ use Symfony\Component\HttpKernel\Tests\Fixtures\ResettableService; class KernelTest extends TestCase { - public static function tearDownAfterClass(): void + protected function tearDown(): void { $fs = new Filesystem(); $fs->remove(__DIR__.'/Fixtures/var');