From f94b7aadd3b4f9cbd393feaa5dd24b248fd73d3a Mon Sep 17 00:00:00 2001 From: Sergey Yastrebov Date: Fri, 20 Apr 2018 13:19:35 +0300 Subject: [PATCH 01/76] fix rounding from string --- .../MoneyToLocalizedStringTransformerTest.php | 10 ++++++++++ .../Component/Intl/NumberFormatter/NumberFormatter.php | 1 + .../NumberFormatter/AbstractNumberFormatterTest.php | 7 +++++++ 3 files changed, 18 insertions(+) diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php index 1ad3aa1615c98..d9fafdff13a35 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php @@ -78,6 +78,16 @@ public function testFloatToIntConversionMismatchOnReversTransform() $transformer = new MoneyToLocalizedStringTransformer(null, null, null, 100); IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_AT'); + $this->assertSame(3655, (int) $transformer->reverseTransform('36,55')); } + + public function testFloatToIntConversionMismatchOnTransform() + { + $transformer = new MoneyToLocalizedStringTransformer(null, null, MoneyToLocalizedStringTransformer::ROUND_DOWN, 100); + IntlTestHelper::requireFullIntl($this, false); + \Locale::setDefault('de_AT'); + + $this->assertSame('10,20', $transformer->transform(1020)); + } } diff --git a/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php b/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php index f68a60143a747..4a9acb5be9ac9 100644 --- a/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php +++ b/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php @@ -708,6 +708,7 @@ private function round($value, $precision) } elseif (isset(self::$customRoundingList[$roundingModeAttribute])) { $roundingCoef = pow(10, $precision); $value *= $roundingCoef; + $value = (float) (string) $value; switch ($roundingModeAttribute) { case self::ROUND_CEILING: diff --git a/src/Symfony/Component/Intl/Tests/NumberFormatter/AbstractNumberFormatterTest.php b/src/Symfony/Component/Intl/Tests/NumberFormatter/AbstractNumberFormatterTest.php index 6c1cc569a3297..6d681f32248bc 100644 --- a/src/Symfony/Component/Intl/Tests/NumberFormatter/AbstractNumberFormatterTest.php +++ b/src/Symfony/Component/Intl/Tests/NumberFormatter/AbstractNumberFormatterTest.php @@ -428,6 +428,7 @@ public function formatRoundingModeRoundHalfUpProvider() // array(1.125, '1.13'), array(1.127, '1.13'), array(1.129, '1.13'), + array(1020 / 100, '10.20'), ); } @@ -451,6 +452,7 @@ public function formatRoundingModeRoundHalfDownProvider() array(1.125, '1.12'), array(1.127, '1.13'), array(1.129, '1.13'), + array(1020 / 100, '10.20'), ); } @@ -474,6 +476,7 @@ public function formatRoundingModeRoundHalfEvenProvider() array(1.125, '1.12'), array(1.127, '1.13'), array(1.129, '1.13'), + array(1020 / 100, '10.20'), ); } @@ -498,6 +501,7 @@ public function formatRoundingModeRoundCeilingProvider() array(-1.123, '-1.12'), array(-1.125, '-1.12'), array(-1.127, '-1.12'), + array(1020 / 100, '10.20'), ); } @@ -522,6 +526,7 @@ public function formatRoundingModeRoundFloorProvider() array(-1.123, '-1.13'), array(-1.125, '-1.13'), array(-1.127, '-1.13'), + array(1020 / 100, '10.20'), ); } @@ -546,6 +551,7 @@ public function formatRoundingModeRoundDownProvider() array(-1.123, '-1.12'), array(-1.125, '-1.12'), array(-1.127, '-1.12'), + array(1020 / 100, '10.20'), ); } @@ -570,6 +576,7 @@ public function formatRoundingModeRoundUpProvider() array(-1.123, '-1.13'), array(-1.125, '-1.13'), array(-1.127, '-1.13'), + array(1020 / 100, '10.20'), ); } From 34f136e01b9f3246f4d8debca0153d9df143761c Mon Sep 17 00:00:00 2001 From: Ben Johnson Date: Thu, 3 May 2018 12:50:56 -0400 Subject: [PATCH 02/76] Suppress warnings when open_basedir is non-empty If PHP is configured *with a non-empty open_basedir* value that does not permit access to the target location, these calls to is_executable() throw warnings. While Symfony may not raise exceptions for warnings in production environments, other frameworks (such as Laravel) do, in which case any of these checks causes a show-stopping 500 error. We fixed a similar issue in the ExecutableFinder class via symfony/symfony#16182 . This has always been an issue, but 709e15e7a37cb7ed6199548dc70dc33168e6cb2d made it more likely that a warning is triggered. --- src/Symfony/Component/Process/PhpExecutableFinder.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Process/PhpExecutableFinder.php b/src/Symfony/Component/Process/PhpExecutableFinder.php index f5c97d6bb9f8f..2a0b7a0268d7f 100644 --- a/src/Symfony/Component/Process/PhpExecutableFinder.php +++ b/src/Symfony/Component/Process/PhpExecutableFinder.php @@ -49,7 +49,7 @@ public function find($includeArgs = true) } if ($php = getenv('PHP_PATH')) { - if (!is_executable($php)) { + if (@!is_executable($php)) { return false; } @@ -57,12 +57,12 @@ public function find($includeArgs = true) } if ($php = getenv('PHP_PEAR_PHP_BIN')) { - if (is_executable($php)) { + if (@is_executable($php)) { return $php; } } - if (is_executable($php = PHP_BINDIR.('\\' === DIRECTORY_SEPARATOR ? '\\php.exe' : '/php'))) { + if (@is_executable($php = PHP_BINDIR.('\\' === DIRECTORY_SEPARATOR ? '\\php.exe' : '/php'))) { return $php; } From 379b8eb56b559acab7d53acad2278277b6846439 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 3 May 2018 18:21:31 -0700 Subject: [PATCH 03/76] [Messenger] Add TransportInterface as first class citizen sender+receiver --- .../FrameworkExtension.php | 15 ++-- .../FrameworkExtensionTest.php | 38 ++++----- .../Transport/AmqpExt/AmqpSender.php | 2 +- .../Transport/AmqpExt/AmqpTransport.php | 79 +++++++++++++++++++ .../AmqpExt/AmqpTransportFactory.php | 14 +--- .../Messenger/Transport/SenderInterface.php | 2 +- ...nsportFactory.php => TransportFactory.php} | 22 +----- .../TransportFactoryInterface.php | 5 +- .../Transport/TransportInterface.php | 21 +++++ 9 files changed, 130 insertions(+), 68 deletions(-) create mode 100644 src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransport.php rename src/Symfony/Component/Messenger/Transport/{Factory/ChainTransportFactory.php => TransportFactory.php} (56%) rename src/Symfony/Component/Messenger/Transport/{Factory => }/TransportFactoryInterface.php (77%) create mode 100644 src/Symfony/Component/Messenger/Transport/TransportInterface.php diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 0d994c5f8f8f0..ff6351e7078e5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -65,6 +65,7 @@ use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Messenger\Transport\ReceiverInterface; use Symfony\Component\Messenger\Transport\SenderInterface; +use Symfony\Component\Messenger\Transport\TransportInterface; use Symfony\Component\PropertyAccess\PropertyAccessor; use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface; use Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface; @@ -1506,19 +1507,13 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder throw new LogicException('The default AMQP transport is not available. Make sure you have installed and enabled the Serializer component. Try enable it or install it by running "composer require symfony/serializer-pack".'); } - $senderDefinition = (new Definition(SenderInterface::class)) - ->setFactory(array(new Reference('messenger.transport_factory'), 'createSender')) - ->setArguments(array($transport['dsn'], $transport['options'])) - ->addTag('messenger.sender', array('name' => $name)) - ; - $container->setDefinition('messenger.sender.'.$name, $senderDefinition); - - $receiverDefinition = (new Definition(ReceiverInterface::class)) - ->setFactory(array(new Reference('messenger.transport_factory'), 'createReceiver')) + $transportDefinition = (new Definition(TransportInterface::class)) + ->setFactory(array(new Reference('messenger.transport_factory'), 'createTransport')) ->setArguments(array($transport['dsn'], $transport['options'])) ->addTag('messenger.receiver', array('name' => $name)) + ->addTag('messenger.sender', array('name' => $name)) ; - $container->setDefinition('messenger.receiver.'.$name, $receiverDefinition); + $container->setDefinition('messenger.transport.'.$name, $transportDefinition); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index d7ce01d39fc21..eebf2b0f1b7f0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -533,30 +533,20 @@ public function testMessenger() public function testMessengerTransports() { $container = $this->createContainerFromFile('messenger_transports'); - $this->assertTrue($container->hasDefinition('messenger.sender.default')); - $this->assertTrue($container->getDefinition('messenger.sender.default')->hasTag('messenger.sender')); - $this->assertEquals(array(array('name' => 'default')), $container->getDefinition('messenger.sender.default')->getTag('messenger.sender')); - $this->assertTrue($container->hasDefinition('messenger.receiver.default')); - $this->assertTrue($container->getDefinition('messenger.receiver.default')->hasTag('messenger.receiver')); - $this->assertEquals(array(array('name' => 'default')), $container->getDefinition('messenger.receiver.default')->getTag('messenger.receiver')); - - $this->assertTrue($container->hasDefinition('messenger.sender.customised')); - $senderFactory = $container->getDefinition('messenger.sender.customised')->getFactory(); - $senderArguments = $container->getDefinition('messenger.sender.customised')->getArguments(); - - $this->assertEquals(array(new Reference('messenger.transport_factory'), 'createSender'), $senderFactory); - $this->assertCount(2, $senderArguments); - $this->assertSame('amqp://localhost/%2f/messages?exchange_name=exchange_name', $senderArguments[0]); - $this->assertSame(array('queue' => array('name' => 'Queue')), $senderArguments[1]); - - $this->assertTrue($container->hasDefinition('messenger.receiver.customised')); - $receiverFactory = $container->getDefinition('messenger.receiver.customised')->getFactory(); - $receiverArguments = $container->getDefinition('messenger.receiver.customised')->getArguments(); - - $this->assertEquals(array(new Reference('messenger.transport_factory'), 'createReceiver'), $receiverFactory); - $this->assertCount(2, $receiverArguments); - $this->assertSame('amqp://localhost/%2f/messages?exchange_name=exchange_name', $receiverArguments[0]); - $this->assertSame(array('queue' => array('name' => 'Queue')), $receiverArguments[1]); + $this->assertTrue($container->hasDefinition('messenger.transport.default')); + $this->assertTrue($container->getDefinition('messenger.transport.default')->hasTag('messenger.receiver')); + $this->assertTrue($container->getDefinition('messenger.transport.default')->hasTag('messenger.sender')); + $this->assertEquals(array(array('name' => 'default')), $container->getDefinition('messenger.transport.default')->getTag('messenger.receiver')); + $this->assertEquals(array(array('name' => 'default')), $container->getDefinition('messenger.transport.default')->getTag('messenger.sender')); + + $this->assertTrue($container->hasDefinition('messenger.transport.customised')); + $transportFactory = $container->getDefinition('messenger.transport.customised')->getFactory(); + $transportArguments = $container->getDefinition('messenger.transport.customised')->getArguments(); + + $this->assertEquals(array(new Reference('messenger.transport_factory'), 'createTransport'), $transportFactory); + $this->assertCount(2, $transportArguments); + $this->assertSame('amqp://localhost/%2f/messages?exchange_name=exchange_name', $transportArguments[0]); + $this->assertSame(array('queue' => array('name' => 'Queue')), $transportArguments[1]); $this->assertTrue($container->hasDefinition('messenger.transport.amqp.factory')); } diff --git a/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpSender.php b/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpSender.php index 0c4bb18f31ca7..523c35db0f11b 100644 --- a/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpSender.php +++ b/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpSender.php @@ -33,7 +33,7 @@ public function __construct(EncoderInterface $messageEncoder, Connection $connec /** * {@inheritdoc} */ - public function send($message) + public function send($message): void { $encodedMessage = $this->messageEncoder->encode($message); diff --git a/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransport.php b/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransport.php new file mode 100644 index 0000000000000..165a795d71ab7 --- /dev/null +++ b/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransport.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Transport\AmqpExt; + +use Symfony\Component\Messenger\Transport\Serialization\DecoderInterface; +use Symfony\Component\Messenger\Transport\Serialization\EncoderInterface; +use Symfony\Component\Messenger\Transport\TransportInterface; + +/** + * @author Nicolas Grekas + */ +class AmqpTransport implements TransportInterface +{ + private $encoder; + private $decoder; + private $dsn; + private $options; + private $debug; + private $connection; + private $receiver; + private $sender; + + public function __construct(EncoderInterface $encoder, DecoderInterface $decoder, string $dsn, array $options, bool $debug) + { + $this->encoder = $encoder; + $this->decoder = $decoder; + $this->dsn = $dsn; + $this->options = $options; + $this->debug = $debug; + } + + /** + * {@inheritdoc} + */ + public function receive(callable $handler): void + { + ($this->receiver ?? $this->getReceiver())->receive($hander); + } + + /** + * {@inheritdoc} + */ + public function stop(): void + { + ($this->receiver ?? $this->getReceiver())->stop(); + } + + /** + * {@inheritdoc} + */ + public function send($message): void + { + ($this->sender ?? $this->getSender())->send($message); + } + + private function getReceiver() + { + return $this->receiver = new AmqpReceiver($this->decoder, $this->connection ?? $this->getConnection()); + } + + private function getSender() + { + return $this->sender = new AmqpSender($this->encoder, $this->connection ?? $this->getConnection()); + } + + private function getConnection() + { + return $this->connection = new Connection($this->dsn, $this->options, $this->debug); + } +} diff --git a/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransportFactory.php b/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransportFactory.php index ddb385af52aef..2da18cb06692c 100644 --- a/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransportFactory.php +++ b/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransportFactory.php @@ -11,11 +11,10 @@ namespace Symfony\Component\Messenger\Transport\AmqpExt; -use Symfony\Component\Messenger\Transport\Factory\TransportFactoryInterface; -use Symfony\Component\Messenger\Transport\ReceiverInterface; -use Symfony\Component\Messenger\Transport\SenderInterface; use Symfony\Component\Messenger\Transport\Serialization\DecoderInterface; use Symfony\Component\Messenger\Transport\Serialization\EncoderInterface; +use Symfony\Component\Messenger\Transport\TransportFactoryInterface; +use Symfony\Component\Messenger\Transport\TransportInterface; /** * @author Samuel Roze @@ -33,14 +32,9 @@ public function __construct(EncoderInterface $encoder, DecoderInterface $decoder $this->debug = $debug; } - public function createReceiver(string $dsn, array $options): ReceiverInterface + public function createTransport(string $dsn, array $options): TransportInterface { - return new AmqpReceiver($this->decoder, Connection::fromDsn($dsn, $options, $this->debug)); - } - - public function createSender(string $dsn, array $options): SenderInterface - { - return new AmqpSender($this->encoder, Connection::fromDsn($dsn, $options, $this->debug)); + return new AmqpTransport($this->encoder, $this->decoder, $dsn, $options, $thid->debug); } public function supports(string $dsn, array $options): bool diff --git a/src/Symfony/Component/Messenger/Transport/SenderInterface.php b/src/Symfony/Component/Messenger/Transport/SenderInterface.php index a142e1f00995e..93b1bd7cbaa3f 100644 --- a/src/Symfony/Component/Messenger/Transport/SenderInterface.php +++ b/src/Symfony/Component/Messenger/Transport/SenderInterface.php @@ -23,5 +23,5 @@ interface SenderInterface * * @param object $message */ - public function send($message); + public function send($message): void; } diff --git a/src/Symfony/Component/Messenger/Transport/Factory/ChainTransportFactory.php b/src/Symfony/Component/Messenger/Transport/TransportFactory.php similarity index 56% rename from src/Symfony/Component/Messenger/Transport/Factory/ChainTransportFactory.php rename to src/Symfony/Component/Messenger/Transport/TransportFactory.php index 779d365dc47e2..2c20533bfb8a9 100644 --- a/src/Symfony/Component/Messenger/Transport/Factory/ChainTransportFactory.php +++ b/src/Symfony/Component/Messenger/Transport/TransportFactory.php @@ -9,15 +9,12 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Messenger\Transport\Factory; - -use Symfony\Component\Messenger\Transport\ReceiverInterface; -use Symfony\Component\Messenger\Transport\SenderInterface; +namespace Symfony\Component\Messenger\Transport; /** * @author Samuel Roze */ -class ChainTransportFactory implements TransportFactoryInterface +class TransportFactory implements TransportFactoryInterface { private $factories; @@ -29,22 +26,11 @@ public function __construct(iterable $factories) $this->factories = $factories; } - public function createReceiver(string $dsn, array $options): ReceiverInterface - { - foreach ($this->factories as $factory) { - if ($factory->supports($dsn, $options)) { - return $factory->createReceiver($dsn, $options); - } - } - - throw new \InvalidArgumentException(sprintf('No transport supports the given DSN "%s".', $dsn)); - } - - public function createSender(string $dsn, array $options): SenderInterface + public function createTransport(string $dsn, array $options): TransportInterface { foreach ($this->factories as $factory) { if ($factory->supports($dsn, $options)) { - return $factory->createSender($dsn, $options); + return $factory->createTransport($dsn, $options); } } diff --git a/src/Symfony/Component/Messenger/Transport/Factory/TransportFactoryInterface.php b/src/Symfony/Component/Messenger/Transport/TransportFactoryInterface.php similarity index 77% rename from src/Symfony/Component/Messenger/Transport/Factory/TransportFactoryInterface.php rename to src/Symfony/Component/Messenger/Transport/TransportFactoryInterface.php index 47ded446bf06c..156eee9839cf6 100644 --- a/src/Symfony/Component/Messenger/Transport/Factory/TransportFactoryInterface.php +++ b/src/Symfony/Component/Messenger/Transport/TransportFactoryInterface.php @@ -9,10 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Messenger\Transport\Factory; - -use Symfony\Component\Messenger\Transport\ReceiverInterface; -use Symfony\Component\Messenger\Transport\SenderInterface; +namespace Symfony\Component\Messenger\Transport; /** * Creates a Messenger transport. diff --git a/src/Symfony/Component/Messenger/Transport/TransportInterface.php b/src/Symfony/Component/Messenger/Transport/TransportInterface.php new file mode 100644 index 0000000000000..066cf5cda7e4b --- /dev/null +++ b/src/Symfony/Component/Messenger/Transport/TransportInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Transport; + +/** + * @author Nicolas Grekas + * + * @experimental in 4.1 + */ +interface TransportInterface extends ReceiverInterface, SenderInterface +{ +} From c3d4536203981d425adc47552c553a9633186a5e Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 7 May 2018 18:01:53 +0200 Subject: [PATCH 04/76] bumped Symfony version to 4.1.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 a45b2b9d02b61..cfaef24ac3a85 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -63,12 +63,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private $requestStackSize = 0; private $resetServices = false; - const VERSION = '4.1.0-BETA1'; + const VERSION = '4.1.0-DEV'; const VERSION_ID = 40100; const MAJOR_VERSION = 4; const MINOR_VERSION = 1; const RELEASE_VERSION = 0; - const EXTRA_VERSION = 'BETA1'; + const EXTRA_VERSION = 'DEV'; const END_OF_MAINTENANCE = '01/2019'; const END_OF_LIFE = '07/2019'; From 5539f9d6c815d4bc1ec7f8191af37f2c8bf0cfae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20Lepp=C3=A4nen?= Date: Mon, 7 May 2018 19:50:17 +0300 Subject: [PATCH 05/76] Fixed return type --- src/Symfony/Component/HttpFoundation/Request.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index e1309d477bd2d..7e8e075a833f3 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -1322,7 +1322,7 @@ public function getRealMethod() * * @param string $format The format * - * @return string The associated mime type (null if not found) + * @return string|null The associated mime type (null if not found) */ public function getMimeType($format) { From 01fe64856f13e530a977cff6870761005118c6f4 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Mon, 7 May 2018 18:47:24 -0400 Subject: [PATCH 06/76] Improved exception on invalid message type --- .../Exception/InvalidArgumentException.php | 19 +++++++++++++++++++ .../Component/Messenger/MessageBus.php | 5 +++++ .../Messenger/Tests/MessageBusTest.php | 9 +++++++++ 3 files changed, 33 insertions(+) create mode 100644 src/Symfony/Component/Messenger/Exception/InvalidArgumentException.php diff --git a/src/Symfony/Component/Messenger/Exception/InvalidArgumentException.php b/src/Symfony/Component/Messenger/Exception/InvalidArgumentException.php new file mode 100644 index 0000000000000..a75c722484c1f --- /dev/null +++ b/src/Symfony/Component/Messenger/Exception/InvalidArgumentException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Exception; + +/** + * @author Yonel Ceruto + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/src/Symfony/Component/Messenger/MessageBus.php b/src/Symfony/Component/Messenger/MessageBus.php index 904eafcdd94f5..f0779b1844852 100644 --- a/src/Symfony/Component/Messenger/MessageBus.php +++ b/src/Symfony/Component/Messenger/MessageBus.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Messenger; +use Symfony\Component\Messenger\Exception\InvalidArgumentException; use Symfony\Component\Messenger\Middleware\MiddlewareInterface; /** @@ -39,6 +40,10 @@ public function __construct(iterable $middlewareHandlers = array()) */ public function dispatch($message) { + if (!\is_object($message)) { + throw new InvalidArgumentException(sprintf('Invalid type for message argument. Expected object, but got "%s".', \gettype($message))); + } + return \call_user_func($this->callableForNextMiddleware(0), $message); } diff --git a/src/Symfony/Component/Messenger/Tests/MessageBusTest.php b/src/Symfony/Component/Messenger/Tests/MessageBusTest.php index e8249b307f806..40eecf9c54e43 100644 --- a/src/Symfony/Component/Messenger/Tests/MessageBusTest.php +++ b/src/Symfony/Component/Messenger/Tests/MessageBusTest.php @@ -26,6 +26,15 @@ public function testItHasTheRightInterface() $this->assertInstanceOf(MessageBusInterface::class, $bus); } + /** + * @expectedException \Symfony\Component\Messenger\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid type for message argument. Expected object, but got "string". + */ + public function testItDispatchInvalidMessageType() + { + (new MessageBus())->dispatch('wrong'); + } + public function testItCallsMiddlewareAndChainTheReturnValue() { $message = new DummyMessage('Hello'); From 0cd51ae26728872f51b62ee1efe9e4a873d3e4c3 Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Mon, 7 May 2018 21:26:27 +0200 Subject: [PATCH 07/76] [Profiler] Fix dump makes toolbar disappear --- .../Resources/views/Profiler/base_js.html.twig | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig index 1553f304dabe4..7088cd87835e9 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig @@ -418,12 +418,10 @@ '{{ path("_wdt", { "token": "xxxxxx" }) }}'.replace(/xxxxxx/, newToken), function(xhr, el) { - /* Evaluate embedded scripts inside the toolbar */ - var i, scripts = [].slice.call(el.querySelectorAll('script')); - - for (i = 0; i < scripts.length; ++i) { - eval(scripts[i].firstChild.nodeValue); - } + /* Evaluate in global scope scripts embedded inside the toolbar */ + eval.call({}, ([].slice.call(el.querySelectorAll('script')).map(function (script) { + return script.firstChild.nodeValue; + }).join('\n'))); el.style.display = -1 !== xhr.responseText.indexOf('sf-toolbarreset') ? 'block' : 'none'; @@ -442,7 +440,7 @@ } /* Handle toolbar-info position */ - var toolbarBlocks = [].slice.call(el.querySelectorAll('.sf-toolbar-block')); + var i, toolbarBlocks = [].slice.call(el.querySelectorAll('.sf-toolbar-block')); for (i = 0; i < toolbarBlocks.length; ++i) { toolbarBlocks[i].onmouseover = function () { var toolbarInfo = this.querySelectorAll('.sf-toolbar-info')[0]; From b8a4d5a2320b90505ae13cd6c3ff4e713211c56e Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Tue, 8 May 2018 10:54:44 -0400 Subject: [PATCH 08/76] Fix AmqpTransport --- .../Component/Messenger/Transport/AmqpExt/AmqpTransport.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransport.php b/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransport.php index 165a795d71ab7..cd5fcdb4899a0 100644 --- a/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransport.php +++ b/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransport.php @@ -43,7 +43,7 @@ public function __construct(EncoderInterface $encoder, DecoderInterface $decoder */ public function receive(callable $handler): void { - ($this->receiver ?? $this->getReceiver())->receive($hander); + ($this->receiver ?? $this->getReceiver())->receive($handler); } /** @@ -74,6 +74,6 @@ private function getSender() private function getConnection() { - return $this->connection = new Connection($this->dsn, $this->options, $this->debug); + return $this->connection = Connection::fromDsn($this->dsn, $this->options, $this->debug); } } From 98967cd73a8cc2a2375f2ca677c9088aa88462ff Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Tue, 8 May 2018 17:08:45 +0200 Subject: [PATCH 09/76] [Messenger] Fix AMQP Transport factory & TransportFactoryInterface --- .../Messenger/Transport/AmqpExt/AmqpTransportFactory.php | 2 +- .../Messenger/Transport/TransportFactoryInterface.php | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransportFactory.php b/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransportFactory.php index 2da18cb06692c..3ffbb561591e1 100644 --- a/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransportFactory.php +++ b/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransportFactory.php @@ -34,7 +34,7 @@ public function __construct(EncoderInterface $encoder, DecoderInterface $decoder public function createTransport(string $dsn, array $options): TransportInterface { - return new AmqpTransport($this->encoder, $this->decoder, $dsn, $options, $thid->debug); + return new AmqpTransport($this->encoder, $this->decoder, $dsn, $options, $this->debug); } public function supports(string $dsn, array $options): bool diff --git a/src/Symfony/Component/Messenger/Transport/TransportFactoryInterface.php b/src/Symfony/Component/Messenger/Transport/TransportFactoryInterface.php index 156eee9839cf6..ac1c72f1b9ad3 100644 --- a/src/Symfony/Component/Messenger/Transport/TransportFactoryInterface.php +++ b/src/Symfony/Component/Messenger/Transport/TransportFactoryInterface.php @@ -20,9 +20,7 @@ */ interface TransportFactoryInterface { - public function createReceiver(string $dsn, array $options): ReceiverInterface; - - public function createSender(string $dsn, array $options): SenderInterface; + public function createTransport(string $dsn, array $options): TransportInterface; public function supports(string $dsn, array $options): bool; } From 7a091d9d614a0830373d2a7ebdeb8c3e906065d6 Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Tue, 8 May 2018 16:44:19 +0100 Subject: [PATCH 10/76] Fix the transport factory after moving it --- .../Bundle/FrameworkBundle/Resources/config/messenger.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml index 28eccb9a89878..145f0543de9ed 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml @@ -60,7 +60,7 @@ - + From fa9b9849b1715d2c8a4cdfb3f1cd61f56f70d6f2 Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Tue, 8 May 2018 17:54:09 +0200 Subject: [PATCH 11/76] [Messenger] Fix default bus name --- .../FrameworkBundle/DependencyInjection/Configuration.php | 2 +- .../Tests/DependencyInjection/ConfigurationTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 4314f17204a1f..a20fde031316e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -1047,7 +1047,7 @@ function ($a) { ->end() ->scalarNode('default_bus')->defaultValue(null)->end() ->arrayNode('buses') - ->defaultValue(array('default' => array('default_middleware' => true, 'middleware' => array()))) + ->defaultValue(array('messenger.bus.default' => array('default_middleware' => true, 'middleware' => array()))) ->useAttributeAsKey('name') ->prototype('array') ->addDefaultsIfNotSet() diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index 8ab7b79172197..d47c1be5526a9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -263,7 +263,7 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor 'encoder' => 'messenger.transport.serializer', 'decoder' => 'messenger.transport.serializer', 'default_bus' => null, - 'buses' => array('default' => array('default_middleware' => true, 'middleware' => array())), + 'buses' => array('messenger.bus.default' => array('default_middleware' => true, 'middleware' => array())), ), ); } From faf9382223ad6ac3e8139ceffd8bc105f9d8a656 Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Tue, 8 May 2018 22:22:44 +0100 Subject: [PATCH 12/76] Add more tests around the AMQP transport --- .../FrameworkExtensionTest.php | 3 + .../AmqpExt/AmqpTransportFactoryTest.php | 48 ++++++++++++++ .../Transport/AmqpExt/AmqpTransportTest.php | 66 +++++++++++++++++++ .../Transport/AmqpExt/AmqpTransport.php | 18 ++--- .../AmqpExt/AmqpTransportFactory.php | 2 +- 5 files changed, 122 insertions(+), 15 deletions(-) create mode 100644 src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpTransportFactoryTest.php create mode 100644 src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpTransportTest.php diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index eebf2b0f1b7f0..8d94d32aa83b0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -36,6 +36,7 @@ use Symfony\Component\HttpKernel\DependencyInjection\LoggerPass; use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; use Symfony\Component\Messenger\Tests\Fixtures\SecondMessage; +use Symfony\Component\Messenger\Transport\TransportFactory; use Symfony\Component\PropertyAccess\PropertyAccessor; use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; use Symfony\Component\Serializer\Normalizer\DateIntervalNormalizer; @@ -528,6 +529,8 @@ public function testMessenger() $container = $this->createContainerFromFile('messenger'); $this->assertTrue($container->hasAlias('message_bus')); $this->assertFalse($container->hasDefinition('messenger.transport.amqp.factory')); + $this->assertTrue($container->hasDefinition('messenger.transport_factory')); + $this->assertSame(TransportFactory::class, $container->getDefinition('messenger.transport_factory')->getClass()); } public function testMessengerTransports() diff --git a/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpTransportFactoryTest.php b/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpTransportFactoryTest.php new file mode 100644 index 0000000000000..53a98e2263a07 --- /dev/null +++ b/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpTransportFactoryTest.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Tests\Transport\AmqpExt; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Messenger\Transport\AmqpExt\AmqpTransport; +use Symfony\Component\Messenger\Transport\AmqpExt\AmqpTransportFactory; +use Symfony\Component\Messenger\Transport\AmqpExt\Connection; +use Symfony\Component\Messenger\Transport\Serialization\DecoderInterface; +use Symfony\Component\Messenger\Transport\Serialization\EncoderInterface; + +class AmqpTransportFactoryTest extends TestCase +{ + public function testSupportsOnlyAmqpTransports() + { + $factory = new AmqpTransportFactory( + $this->getMockBuilder(EncoderInterface::class)->getMock(), + $this->getMockBuilder(DecoderInterface::class)->getMock(), + true + ); + + $this->assertTrue($factory->supports('amqp://localhost', array())); + $this->assertFalse($factory->supports('sqs://localhost', array())); + $this->assertFalse($factory->supports('invalid-dsn', array())); + } + + public function testItCreatesTheTransport() + { + $factory = new AmqpTransportFactory( + $encoder = $this->getMockBuilder(EncoderInterface::class)->getMock(), + $decoder = $this->getMockBuilder(DecoderInterface::class)->getMock(), + true + ); + + $expectedTransport = new AmqpTransport($encoder, $decoder, Connection::fromDsn('amqp://localhost', array('foo' => 'bar'), true), array('foo' => 'bar'), true); + + $this->assertEquals($expectedTransport, $factory->createTransport('amqp://localhost', array('foo' => 'bar'))); + } +} diff --git a/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpTransportTest.php b/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpTransportTest.php new file mode 100644 index 0000000000000..26f3261577cae --- /dev/null +++ b/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpTransportTest.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Tests\Transport\AmqpExt; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; +use Symfony\Component\Messenger\Transport\AmqpExt\AmqpTransport; +use Symfony\Component\Messenger\Transport\AmqpExt\Connection; +use Symfony\Component\Messenger\Transport\Serialization\DecoderInterface; +use Symfony\Component\Messenger\Transport\Serialization\EncoderInterface; +use Symfony\Component\Messenger\Transport\TransportInterface; + +/** + * @requires extension amqp + */ +class AmqpTransportTest extends TestCase +{ + public function testItIsATransport() + { + $transport = $this->getTransport(); + + $this->assertInstanceOf(TransportInterface::class, $transport); + } + + public function testReceivesMessages() + { + $transport = $this->getTransport( + null, + $decoder = $this->getMockBuilder(DecoderInterface::class)->getMock(), + $connection = $this->getMockBuilder(Connection::class)->disableOriginalConstructor()->getMock() + ); + + $decodedMessage = new DummyMessage('Decoded.'); + + $amqpEnvelope = $this->getMockBuilder(\AMQPEnvelope::class)->getMock(); + $amqpEnvelope->method('getBody')->willReturn('body'); + $amqpEnvelope->method('getHeaders')->willReturn(array('my' => 'header')); + + $decoder->method('decode')->with(array('body' => 'body', 'headers' => array('my' => 'header')))->willReturn($decodedMessage); + $connection->method('get')->willReturn($amqpEnvelope); + + $transport->receive(function ($message) use ($transport, $decodedMessage) { + $this->assertSame($decodedMessage, $message); + + $transport->stop(); + }); + } + + private function getTransport(EncoderInterface $encoder = null, DecoderInterface $decoder = null, Connection $connection = null) + { + $encoder = $encoder ?: $this->getMockBuilder(EncoderInterface::class)->getMock(); + $decoder = $decoder ?: $this->getMockBuilder(DecoderInterface::class)->getMock(); + $connection = $connection ?: $this->getMockBuilder(Connection::class)->disableOriginalConstructor()->getMock(); + + return new AmqpTransport($encoder, $decoder, $connection); + } +} diff --git a/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransport.php b/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransport.php index cd5fcdb4899a0..583ec03baefbb 100644 --- a/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransport.php +++ b/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransport.php @@ -22,20 +22,15 @@ class AmqpTransport implements TransportInterface { private $encoder; private $decoder; - private $dsn; - private $options; - private $debug; private $connection; private $receiver; private $sender; - public function __construct(EncoderInterface $encoder, DecoderInterface $decoder, string $dsn, array $options, bool $debug) + public function __construct(EncoderInterface $encoder, DecoderInterface $decoder, Connection $connection) { $this->encoder = $encoder; $this->decoder = $decoder; - $this->dsn = $dsn; - $this->options = $options; - $this->debug = $debug; + $this->connection = $connection; } /** @@ -64,16 +59,11 @@ public function send($message): void private function getReceiver() { - return $this->receiver = new AmqpReceiver($this->decoder, $this->connection ?? $this->getConnection()); + return $this->receiver = new AmqpReceiver($this->decoder, $this->connection); } private function getSender() { - return $this->sender = new AmqpSender($this->encoder, $this->connection ?? $this->getConnection()); - } - - private function getConnection() - { - return $this->connection = Connection::fromDsn($this->dsn, $this->options, $this->debug); + return $this->sender = new AmqpSender($this->encoder, $this->connection); } } diff --git a/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransportFactory.php b/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransportFactory.php index 3ffbb561591e1..29fb4ae4aa3e0 100644 --- a/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransportFactory.php +++ b/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransportFactory.php @@ -34,7 +34,7 @@ public function __construct(EncoderInterface $encoder, DecoderInterface $decoder public function createTransport(string $dsn, array $options): TransportInterface { - return new AmqpTransport($this->encoder, $this->decoder, $dsn, $options, $this->debug); + return new AmqpTransport($this->encoder, $this->decoder, Connection::fromDsn($dsn, $options, $this->debug)); } public function supports(string $dsn, array $options): bool From 9847b8372327394f9070479684e2c30ea0f35354 Mon Sep 17 00:00:00 2001 From: Roland Franssen Date: Wed, 4 Apr 2018 19:53:00 +0200 Subject: [PATCH 13/76] [Messenger] Add debug:messenger CLI command --- .../FrameworkExtension.php | 1 + .../Resources/config/console.xml | 5 ++ .../Command/MessengerDebugCommand.php | 81 +++++++++++++++++++ .../DependencyInjection/MessengerPass.php | 17 ++-- 4 files changed, 97 insertions(+), 7 deletions(-) create mode 100644 src/Symfony/Component/Messenger/Command/MessengerDebugCommand.php diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index d8f4b61699a9e..dafdc40219f95 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -277,6 +277,7 @@ public function load(array $configs, ContainerBuilder $container) $this->registerMessengerConfiguration($config['messenger'], $container, $loader, $config['serializer'], $config['validation']); } else { $container->removeDefinition('console.command.messenger_consume_messages'); + $container->removeDefinition('console.command.messenger_debug'); } if ($this->isConfigEnabled($container, $config['web_link'])) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml index cb71bbb8de8f7..40da27e075da3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml @@ -77,6 +77,11 @@ + + + + + diff --git a/src/Symfony/Component/Messenger/Command/MessengerDebugCommand.php b/src/Symfony/Component/Messenger/Command/MessengerDebugCommand.php new file mode 100644 index 0000000000000..544538dcf8791 --- /dev/null +++ b/src/Symfony/Component/Messenger/Command/MessengerDebugCommand.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +/** + * A console command to debug Messenger information. + * + * @author Roland Franssen + * + * @experimental in 4.1 + */ +class MessengerDebugCommand extends Command +{ + protected static $defaultName = 'debug:messenger'; + + private $mapping; + + public function __construct(array $mapping) + { + parent::__construct(); + + $this->mapping = $mapping; + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setDescription('Lists messages you can dispatch using the message bus') + ->setHelp(<<<'EOF' +The %command.name% command displays all messages that can be +dispatched using the message bus: + + php %command.full_name% + +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + $io->title('Messenger'); + $io->text('The following messages can be dispatched:'); + $io->newLine(); + + $tableRows = array(); + foreach ($this->mapping as $message => $handlers) { + $tableRows[] = array(sprintf('%s', $message)); + foreach ($handlers as $handler) { + $tableRows[] = array(sprintf(' handled by %s', $handler)); + } + } + + if ($tableRows) { + $io->table(array(), $tableRows); + } else { + $io->text('No messages were found that have valid handlers.'); + } + } +} diff --git a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php index 1fd76db8fee25..2af6577f485a8 100644 --- a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php +++ b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php @@ -104,26 +104,29 @@ private function registerHandlers(ContainerBuilder $container) } $definitions = array(); + $handlersLocatorMapping = array(); foreach ($handlersByMessage as $message => $handlers) { if (1 === \count($handlers)) { - $handlersByMessage[$message] = current($handlers); + $handlersLocatorMapping['handler.'.$message] = current($handlers); } else { $d = new Definition(ChainHandler::class, array($handlers)); $d->setPrivate(true); $serviceId = hash('sha1', $message); $definitions[$serviceId] = $d; - $handlersByMessage[$message] = new Reference($serviceId); + $handlersLocatorMapping['handler.'.$message] = new Reference($serviceId); } } $container->addDefinitions($definitions); - $handlersLocatorMapping = array(); - foreach ($handlersByMessage as $message => $handler) { - $handlersLocatorMapping['handler.'.$message] = $handler; - } - $handlerResolver = $container->getDefinition('messenger.handler_resolver'); $handlerResolver->replaceArgument(0, ServiceLocatorTagPass::register($container, $handlersLocatorMapping)); + + if ($container->hasDefinition('console.command.messenger_debug')) { + $container->getDefinition('console.command.messenger_debug') + ->replaceArgument(0, array_map(function (array $handlers): array { + return array_map('strval', $handlers); + }, $handlersByMessage)); + } } private function guessHandledClasses(\ReflectionClass $handlerClass, string $serviceId): array From 290c7eb1bcf9cd9a17d66e63ca226eed191917ec Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Wed, 9 May 2018 09:31:45 +0100 Subject: [PATCH 14/76] Rename the command `DebugCommand` --- src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml | 2 +- .../Command/{MessengerDebugCommand.php => DebugCommand.php} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/Symfony/Component/Messenger/Command/{MessengerDebugCommand.php => DebugCommand.php} (97%) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml index 40da27e075da3..21fe28375f6fb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml @@ -77,7 +77,7 @@ - + diff --git a/src/Symfony/Component/Messenger/Command/MessengerDebugCommand.php b/src/Symfony/Component/Messenger/Command/DebugCommand.php similarity index 97% rename from src/Symfony/Component/Messenger/Command/MessengerDebugCommand.php rename to src/Symfony/Component/Messenger/Command/DebugCommand.php index 544538dcf8791..f41747dc0f649 100644 --- a/src/Symfony/Component/Messenger/Command/MessengerDebugCommand.php +++ b/src/Symfony/Component/Messenger/Command/DebugCommand.php @@ -23,7 +23,7 @@ * * @experimental in 4.1 */ -class MessengerDebugCommand extends Command +class DebugCommand extends Command { protected static $defaultName = 'debug:messenger'; From 09987102a8a92cdd2c6749edbe9c5709b5e14237 Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Wed, 9 May 2018 13:16:14 +0200 Subject: [PATCH 15/76] [Profiler] Join using ';\n' --- .../Resources/views/Profiler/base_js.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig index 7088cd87835e9..0c5967b4e75ce 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig @@ -421,7 +421,7 @@ /* Evaluate in global scope scripts embedded inside the toolbar */ eval.call({}, ([].slice.call(el.querySelectorAll('script')).map(function (script) { return script.firstChild.nodeValue; - }).join('\n'))); + }).join(';\n'))); el.style.display = -1 !== xhr.responseText.indexOf('sf-toolbarreset') ? 'block' : 'none'; From 7f87309c10653dcfeb9a46312959cba6380ae670 Mon Sep 17 00:00:00 2001 From: Roland Franssen Date: Wed, 9 May 2018 13:00:56 +0200 Subject: [PATCH 16/76] fix deps --- src/Symfony/Bundle/FrameworkBundle/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 8c1c32194fdea..3dfca6a069709 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -41,7 +41,7 @@ "symfony/security": "~3.4|~4.0", "symfony/form": "^4.1", "symfony/expression-language": "~3.4|~4.0", - "symfony/messenger": "^4.1", + "symfony/messenger": "^4.1-beta2", "symfony/process": "~3.4|~4.0", "symfony/security-core": "~3.4|~4.0", "symfony/security-csrf": "~3.4|~4.0", From 8315b868d56e0b870b502748e0ac8bfc8792d087 Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Tue, 8 May 2018 20:53:21 +0100 Subject: [PATCH 17/76] [Messenger][DX] Uses a default receiver when only one is defined --- .../Resources/config/console.xml | 1 + .../Command/ConsumeMessagesCommand.php | 6 ++-- .../DependencyInjection/MessengerPass.php | 16 ++++++--- .../DependencyInjection/MessengerPassTest.php | 36 +++++++++++++++++++ 4 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml index cb71bbb8de8f7..da87f2ea15c19 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml @@ -73,6 +73,7 @@ + null diff --git a/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php b/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php index 94684aacfbce2..72dc2aa907f2d 100644 --- a/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php +++ b/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php @@ -37,14 +37,16 @@ class ConsumeMessagesCommand extends Command private $bus; private $receiverLocator; private $logger; + private $defaultReceiverName; - public function __construct(MessageBusInterface $bus, ContainerInterface $receiverLocator, LoggerInterface $logger = null) + public function __construct(MessageBusInterface $bus, ContainerInterface $receiverLocator, LoggerInterface $logger = null, string $defaultReceiverName = null) { parent::__construct(); $this->bus = $bus; $this->receiverLocator = $receiverLocator; $this->logger = $logger; + $this->defaultReceiverName = $defaultReceiverName; } /** @@ -54,7 +56,7 @@ protected function configure(): void { $this ->setDefinition(array( - new InputArgument('receiver', InputArgument::REQUIRED, 'Name of the receiver'), + new InputArgument('receiver', $this->defaultReceiverName ? InputArgument::OPTIONAL : InputArgument::REQUIRED, 'Name of the receiver', $this->defaultReceiverName), new InputOption('limit', 'l', InputOption::VALUE_REQUIRED, 'Limit the number of received messages'), new InputOption('memory-limit', 'm', InputOption::VALUE_REQUIRED, 'The memory limit the worker can consume'), new InputOption('time-limit', 't', InputOption::VALUE_REQUIRED, 'The time limit in seconds the worker can run'), diff --git a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php index 62f63dc05894d..107234dad956e 100644 --- a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php +++ b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php @@ -161,16 +161,22 @@ private function guessHandledClasses(\ReflectionClass $handlerClass, string $ser private function registerReceivers(ContainerBuilder $container) { $receiverMapping = array(); - foreach ($container->findTaggedServiceIds($this->receiverTag) as $id => $tags) { - foreach ($tags as $tag) { - $receiverMapping[$id] = new Reference($id); + $taggedReceivers = $container->findTaggedServiceIds($this->receiverTag); + + foreach ($taggedReceivers as $id => $tags) { + $receiverMapping[$id] = new Reference($id); + foreach ($tags as $tag) { if (isset($tag['name'])) { $receiverMapping[$tag['name']] = $receiverMapping[$id]; } } } + if (1 === \count($taggedReceivers) && $container->hasDefinition('console.command.messenger_consume_messages')) { + $container->getDefinition('console.command.messenger_consume_messages')->replaceArgument(3, (string) current($receiverMapping)); + } + $container->getDefinition('messenger.receiver_locator')->replaceArgument(0, $receiverMapping); } @@ -178,9 +184,9 @@ private function registerSenders(ContainerBuilder $container) { $senderLocatorMapping = array(); foreach ($container->findTaggedServiceIds($this->senderTag) as $id => $tags) { - foreach ($tags as $tag) { - $senderLocatorMapping[$id] = new Reference($id); + $senderLocatorMapping[$id] = new Reference($id); + foreach ($tags as $tag) { if (isset($tag['name'])) { $senderLocatorMapping[$tag['name']] = $senderLocatorMapping[$id]; } diff --git a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php index b230a9c6df60a..37c9aefed4a4d 100644 --- a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php +++ b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php @@ -16,6 +16,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ServiceLocator; +use Symfony\Component\Messenger\Command\ConsumeMessagesCommand; use Symfony\Component\Messenger\Transport\AmqpExt\AmqpReceiver; use Symfony\Component\Messenger\Transport\AmqpExt\AmqpSender; use Symfony\Component\Messenger\Handler\Locator\ContainerHandlerLocator; @@ -116,6 +117,41 @@ public function testItRegistersReceiversWithoutTagName() $this->assertEquals(array(AmqpReceiver::class => new Reference(AmqpReceiver::class)), $container->getDefinition('messenger.receiver_locator')->getArgument(0)); } + public function testItRegistersOneReceiverAndSetsTheDefaultOneOnTheCommand() + { + $container = $this->getContainerBuilder(); + $container->register('console.command.messenger_consume_messages', ConsumeMessagesCommand::class)->setArguments(array( + new Reference('message_bus'), + new Reference('messenger.receiver_locator'), + null, + null, + )); + + $container->register(AmqpReceiver::class, AmqpReceiver::class)->addTag('messenger.receiver', array('name' => 'amqp')); + + (new MessengerPass())->process($container); + + $this->assertSame(AmqpReceiver::class, $container->getDefinition('console.command.messenger_consume_messages')->getArgument(3)); + } + + public function testItRegistersMultipleReceiversAndDoesNotSetTheDefaultOneOnTheCommand() + { + $container = $this->getContainerBuilder(); + $container->register('console.command.messenger_consume_messages', ConsumeMessagesCommand::class)->setArguments(array( + new Reference('message_bus'), + new Reference('messenger.receiver_locator'), + null, + null, + )); + + $container->register(AmqpReceiver::class, AmqpReceiver::class)->addTag('messenger.receiver', array('name' => 'amqp')); + $container->register(DummyReceiver::class, DummyReceiver::class)->addTag('messenger.receiver', array('name' => 'dummy')); + + (new MessengerPass())->process($container); + + $this->assertNull($container->getDefinition('console.command.messenger_consume_messages')->getArgument(3)); + } + public function testItRegistersSenders() { $container = $this->getContainerBuilder(); From 7c33cb27abd1f809e220f1d7ec622f918de3e353 Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Mon, 7 May 2018 16:30:59 +0100 Subject: [PATCH 18/76] feature #26945 [Messenger] Support configuring messages when dispatching (ogizanagi) This reverts commit 76b17b0e0f082b67236a9ba1aa61a8092d38d350. --- .../Middleware/SendMessageMiddleware.php | 14 ++-- .../Transport/ReceivedMessage.php | 16 ++-- .../Transport/WrapIntoReceivedMessage.php | 9 +- src/Symfony/Component/Messenger/Envelope.php | 80 ++++++++++++++++++ .../Messenger/EnvelopeAwareInterface.php | 23 ++++++ .../Messenger/EnvelopeItemInterface.php | 24 ++++++ .../Component/Messenger/MessageBus.php | 6 ++ .../Messenger/MessageBusInterface.php | 2 +- .../Configuration/ValidationConfiguration.php | 58 +++++++++++++ .../Middleware/LoggingMiddleware.php | 5 -- .../Middleware/ValidationMiddleware.php | 17 +++- .../Middleware/SendMessageMiddlewareTest.php | 28 +++++-- .../SerializerConfigurationTest.php | 30 +++++++ .../DependencyInjection/MessengerPassTest.php | 3 +- .../Messenger/Tests/EnvelopeTest.php | 82 +++++++++++++++++++ .../ValidationConfigurationTest.php | 38 +++++++++ .../Middleware/ValidationMiddlewareTest.php | 26 ++++++ .../AmqpExt/AmqpExtIntegrationTest.php | 26 ++++-- .../Transport/AmqpExt/AmqpReceiverTest.php | 5 +- .../Transport/AmqpExt/AmqpSenderTest.php | 7 +- .../AmqpExt/Fixtures/long_receiver.php | 10 +-- ...pWhenMemoryUsageIsExceededReceiverTest.php | 5 +- ...WhenMessageCountIsExceededReceiverTest.php | 9 +- .../Serialization/SerializerTest.php | 49 +++++++++-- .../Component/Messenger/Tests/WorkerTest.php | 25 +++--- .../Transport/AmqpExt/AmqpReceiver.php | 22 ++--- .../Transport/AmqpExt/AmqpSender.php | 11 +-- .../StopWhenMemoryUsageIsExceededReceiver.php | 5 +- ...StopWhenMessageCountIsExceededReceiver.php | 7 +- .../Messenger/Transport/ReceiverInterface.php | 6 +- .../Messenger/Transport/SenderInterface.php | 8 +- .../Serialization/DecoderInterface.php | 12 +-- .../Serialization/EncoderInterface.php | 9 +- .../Transport/Serialization/Serializer.php | 40 +++++++-- .../Serialization/SerializerConfiguration.php | 46 +++++++++++ src/Symfony/Component/Messenger/Worker.php | 10 +-- 36 files changed, 648 insertions(+), 125 deletions(-) create mode 100644 src/Symfony/Component/Messenger/Envelope.php create mode 100644 src/Symfony/Component/Messenger/EnvelopeAwareInterface.php create mode 100644 src/Symfony/Component/Messenger/EnvelopeItemInterface.php create mode 100644 src/Symfony/Component/Messenger/Middleware/Configuration/ValidationConfiguration.php create mode 100644 src/Symfony/Component/Messenger/Tests/Asynchronous/Transport/Serialization/SerializerConfigurationTest.php create mode 100644 src/Symfony/Component/Messenger/Tests/EnvelopeTest.php create mode 100644 src/Symfony/Component/Messenger/Tests/Middleware/Configuration/ValidationConfigurationTest.php create mode 100644 src/Symfony/Component/Messenger/Transport/Serialization/SerializerConfiguration.php diff --git a/src/Symfony/Component/Messenger/Asynchronous/Middleware/SendMessageMiddleware.php b/src/Symfony/Component/Messenger/Asynchronous/Middleware/SendMessageMiddleware.php index eeba363cc87c2..9aabe794a11ab 100644 --- a/src/Symfony/Component/Messenger/Asynchronous/Middleware/SendMessageMiddleware.php +++ b/src/Symfony/Component/Messenger/Asynchronous/Middleware/SendMessageMiddleware.php @@ -13,12 +13,14 @@ use Symfony\Component\Messenger\Asynchronous\Routing\SenderLocatorInterface; use Symfony\Component\Messenger\Asynchronous\Transport\ReceivedMessage; +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\EnvelopeAwareInterface; use Symfony\Component\Messenger\Middleware\MiddlewareInterface; /** * @author Samuel Roze */ -class SendMessageMiddleware implements MiddlewareInterface +class SendMessageMiddleware implements MiddlewareInterface, EnvelopeAwareInterface { private $senderLocator; @@ -32,17 +34,19 @@ public function __construct(SenderLocatorInterface $senderLocator) */ public function handle($message, callable $next) { - if ($message instanceof ReceivedMessage) { - return $next($message->getMessage()); + $envelope = Envelope::wrap($message); + if ($envelope->get(ReceivedMessage::class)) { + // It's a received message. Do not send it back: + return $next($message); } - if (!empty($senders = $this->senderLocator->getSendersForMessage($message))) { + if (!empty($senders = $this->senderLocator->getSendersForMessage($envelope->getMessage()))) { foreach ($senders as $sender) { if (null === $sender) { continue; } - $sender->send($message); + $sender->send($envelope); } if (!\in_array(null, $senders, true)) { diff --git a/src/Symfony/Component/Messenger/Asynchronous/Transport/ReceivedMessage.php b/src/Symfony/Component/Messenger/Asynchronous/Transport/ReceivedMessage.php index 1b1298da63d32..c713a589ad53c 100644 --- a/src/Symfony/Component/Messenger/Asynchronous/Transport/ReceivedMessage.php +++ b/src/Symfony/Component/Messenger/Asynchronous/Transport/ReceivedMessage.php @@ -12,26 +12,26 @@ namespace Symfony\Component\Messenger\Asynchronous\Transport; use Symfony\Component\Messenger\Asynchronous\Middleware\SendMessageMiddleware; +use Symfony\Component\Messenger\EnvelopeItemInterface; /** - * Wraps a received message. This is mainly used by the `SendMessageMiddleware` middleware to identify + * Marker config for a received message. + * This is mainly used by the `SendMessageMiddleware` middleware to identify * a message should not be sent if it was just received. * * @see SendMessageMiddleware * * @author Samuel Roze */ -final class ReceivedMessage +final class ReceivedMessage implements EnvelopeItemInterface { - private $message; - - public function __construct($message) + public function serialize() { - $this->message = $message; + return ''; } - public function getMessage() + public function unserialize($serialized) { - return $this->message; + // noop } } diff --git a/src/Symfony/Component/Messenger/Asynchronous/Transport/WrapIntoReceivedMessage.php b/src/Symfony/Component/Messenger/Asynchronous/Transport/WrapIntoReceivedMessage.php index 8af87a2a4514b..4b10d6445fc5f 100644 --- a/src/Symfony/Component/Messenger/Asynchronous/Transport/WrapIntoReceivedMessage.php +++ b/src/Symfony/Component/Messenger/Asynchronous/Transport/WrapIntoReceivedMessage.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Messenger\Asynchronous\Transport; +use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Transport\ReceiverInterface; /** @@ -27,12 +28,12 @@ public function __construct(ReceiverInterface $decoratedConsumer) public function receive(callable $handler): void { - $this->decoratedReceiver->receive(function ($message) use ($handler) { - if (null !== $message) { - $message = new ReceivedMessage($message); + $this->decoratedReceiver->receive(function (?Envelope $envelope) use ($handler) { + if (null !== $envelope) { + $envelope = $envelope->with(new ReceivedMessage()); } - $handler($message); + $handler($envelope); }); } diff --git a/src/Symfony/Component/Messenger/Envelope.php b/src/Symfony/Component/Messenger/Envelope.php new file mode 100644 index 0000000000000..14778593c4baf --- /dev/null +++ b/src/Symfony/Component/Messenger/Envelope.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger; + +/** + * A message wrapped in an envelope with items (configurations, markers, ...). + * + * @author Maxime Steinhausser + * + * @experimental in 4.1 + */ +final class Envelope +{ + private $items = array(); + private $message; + + /** + * @param object $message + * @param EnvelopeItemInterface[] $items + */ + public function __construct($message, array $items = array()) + { + $this->message = $message; + foreach ($items as $item) { + $this->items[\get_class($item)] = $item; + } + } + + /** + * Wrap a message into an envelope if not already wrapped. + * + * @param Envelope|object $message + */ + public static function wrap($message): self + { + return $message instanceof self ? $message : new self($message); + } + + /** + * @return Envelope a new Envelope instance with additional item + */ + public function with(EnvelopeItemInterface $item): self + { + $cloned = clone $this; + + $cloned->items[\get_class($item)] = $item; + + return $cloned; + } + + public function get(string $itemFqcn): ?EnvelopeItemInterface + { + return $this->items[$itemFqcn] ?? null; + } + + /** + * @return EnvelopeItemInterface[] indexed by fqcn + */ + public function all(): array + { + return $this->items; + } + + /** + * @return object The original message contained in the envelope + */ + public function getMessage() + { + return $this->message; + } +} diff --git a/src/Symfony/Component/Messenger/EnvelopeAwareInterface.php b/src/Symfony/Component/Messenger/EnvelopeAwareInterface.php new file mode 100644 index 0000000000000..c19bc8436286a --- /dev/null +++ b/src/Symfony/Component/Messenger/EnvelopeAwareInterface.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger; + +/** + * A Messenger protagonist aware of the message envelope and its content. + * + * @author Maxime Steinhausser + * + * @experimental in 4.1 + */ +interface EnvelopeAwareInterface +{ +} diff --git a/src/Symfony/Component/Messenger/EnvelopeItemInterface.php b/src/Symfony/Component/Messenger/EnvelopeItemInterface.php new file mode 100644 index 0000000000000..2561a127546c0 --- /dev/null +++ b/src/Symfony/Component/Messenger/EnvelopeItemInterface.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger; + +/** + * An envelope item related to a message. + * This item must be serializable for transport. + * + * @author Maxime Steinhausser + * + * @experimental in 4.1 + */ +interface EnvelopeItemInterface extends \Serializable +{ +} diff --git a/src/Symfony/Component/Messenger/MessageBus.php b/src/Symfony/Component/Messenger/MessageBus.php index f0779b1844852..c467a6f79dacb 100644 --- a/src/Symfony/Component/Messenger/MessageBus.php +++ b/src/Symfony/Component/Messenger/MessageBus.php @@ -60,6 +60,12 @@ private function callableForNextMiddleware(int $index): callable $middleware = $this->indexedMiddlewareHandlers[$index]; return function ($message) use ($middleware, $index) { + $message = Envelope::wrap($message); + if (!$middleware instanceof EnvelopeAwareInterface) { + // Do not provide the envelope if the middleware cannot read it: + $message = $message->getMessage(); + } + return $middleware->handle($message, $this->callableForNextMiddleware($index + 1)); }; } diff --git a/src/Symfony/Component/Messenger/MessageBusInterface.php b/src/Symfony/Component/Messenger/MessageBusInterface.php index 1d441ea568ff7..8c2b91d1af3ae 100644 --- a/src/Symfony/Component/Messenger/MessageBusInterface.php +++ b/src/Symfony/Component/Messenger/MessageBusInterface.php @@ -23,7 +23,7 @@ interface MessageBusInterface * * The bus can return a value coming from handlers, but is not required to do so. * - * @param object $message + * @param object|Envelope $message The message or the message pre-wrapped in an envelope * * @return mixed */ diff --git a/src/Symfony/Component/Messenger/Middleware/Configuration/ValidationConfiguration.php b/src/Symfony/Component/Messenger/Middleware/Configuration/ValidationConfiguration.php new file mode 100644 index 0000000000000..1b6180857b31d --- /dev/null +++ b/src/Symfony/Component/Messenger/Middleware/Configuration/ValidationConfiguration.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Middleware\Configuration; + +use Symfony\Component\Messenger\EnvelopeItemInterface; +use Symfony\Component\Validator\Constraints\GroupSequence; + +/** + * @author Maxime Steinhausser + * + * @experimental in 4.1 + */ +final class ValidationConfiguration implements EnvelopeItemInterface +{ + private $groups; + + /** + * @param string[]|GroupSequence $groups + */ + public function __construct($groups) + { + $this->groups = $groups; + } + + public function getGroups() + { + return $this->groups; + } + + public function serialize() + { + $isGroupSequence = $this->groups instanceof GroupSequence; + + return serialize(array( + 'groups' => $isGroupSequence ? $this->groups->groups : $this->groups, + 'is_group_sequence' => $isGroupSequence, + )); + } + + public function unserialize($serialized) + { + list( + 'groups' => $groups, + 'is_group_sequence' => $isGroupSequence + ) = unserialize($serialized, array('allowed_classes' => false)); + + $this->__construct($isGroupSequence ? new GroupSequence($groups) : $groups); + } +} diff --git a/src/Symfony/Component/Messenger/Middleware/LoggingMiddleware.php b/src/Symfony/Component/Messenger/Middleware/LoggingMiddleware.php index 4de6e42575805..ebaf8525c0407 100644 --- a/src/Symfony/Component/Messenger/Middleware/LoggingMiddleware.php +++ b/src/Symfony/Component/Messenger/Middleware/LoggingMiddleware.php @@ -11,7 +11,6 @@ namespace Symfony\Component\Messenger\Middleware; -use Symfony\Component\Messenger\Asynchronous\Transport\ReceivedMessage; use Psr\Log\LoggerInterface; /** @@ -51,10 +50,6 @@ public function handle($message, callable $next) private function createContext($message): array { - if ($message instanceof ReceivedMessage) { - $message = $message->getMessage(); - } - return array( 'message' => $message, 'class' => \get_class($message), diff --git a/src/Symfony/Component/Messenger/Middleware/ValidationMiddleware.php b/src/Symfony/Component/Messenger/Middleware/ValidationMiddleware.php index 3b168367cdd33..e588d9256bd32 100644 --- a/src/Symfony/Component/Messenger/Middleware/ValidationMiddleware.php +++ b/src/Symfony/Component/Messenger/Middleware/ValidationMiddleware.php @@ -11,13 +11,16 @@ namespace Symfony\Component\Messenger\Middleware; +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\EnvelopeAwareInterface; use Symfony\Component\Messenger\Exception\ValidationFailedException; +use Symfony\Component\Messenger\Middleware\Configuration\ValidationConfiguration; use Symfony\Component\Validator\Validator\ValidatorInterface; /** * @author Tobias Nyholm */ -class ValidationMiddleware implements MiddlewareInterface +class ValidationMiddleware implements MiddlewareInterface, EnvelopeAwareInterface { private $validator; @@ -28,9 +31,17 @@ public function __construct(ValidatorInterface $validator) public function handle($message, callable $next) { - $violations = $this->validator->validate($message); + $envelope = Envelope::wrap($message); + $subject = $envelope->getMessage(); + $groups = null; + /** @var ValidationConfiguration|null $validationConfig */ + if ($validationConfig = $envelope->get(ValidationConfiguration::class)) { + $groups = $validationConfig->getGroups(); + } + + $violations = $this->validator->validate($subject, null, $groups); if (\count($violations)) { - throw new ValidationFailedException($message, $violations); + throw new ValidationFailedException($subject, $violations); } return $next($message); diff --git a/src/Symfony/Component/Messenger/Tests/Asynchronous/Middleware/SendMessageMiddlewareTest.php b/src/Symfony/Component/Messenger/Tests/Asynchronous/Middleware/SendMessageMiddlewareTest.php index 6398aff361684..aa22741c397b6 100644 --- a/src/Symfony/Component/Messenger/Tests/Asynchronous/Middleware/SendMessageMiddlewareTest.php +++ b/src/Symfony/Component/Messenger/Tests/Asynchronous/Middleware/SendMessageMiddlewareTest.php @@ -15,6 +15,7 @@ use Symfony\Component\Messenger\Asynchronous\Middleware\SendMessageMiddleware; use Symfony\Component\Messenger\Asynchronous\Routing\SenderLocatorInterface; use Symfony\Component\Messenger\Asynchronous\Transport\ReceivedMessage; +use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; use Symfony\Component\Messenger\Transport\SenderInterface; @@ -30,12 +31,28 @@ public function testItSendsTheMessageToAssignedSender() $sender, ))); - $sender->expects($this->once())->method('send')->with($message); + $sender->expects($this->once())->method('send')->with(Envelope::wrap($message)); $next->expects($this->never())->method($this->anything()); $middleware->handle($message, $next); } + public function testItSendsTheMessageToAssignedSenderWithPreWrappedMessage() + { + $envelope = Envelope::wrap(new DummyMessage('Hey')); + $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); + $next = $this->createPartialMock(\stdClass::class, array('__invoke')); + + $middleware = new SendMessageMiddleware(new InMemorySenderLocator(array( + $sender, + ))); + + $sender->expects($this->once())->method('send')->with($envelope); + $next->expects($this->never())->method($this->anything()); + + $middleware->handle($envelope, $next); + } + public function testItAlsoCallsTheNextMiddlewareIfASenderIsNull() { $message = new DummyMessage('Hey'); @@ -47,7 +64,7 @@ public function testItAlsoCallsTheNextMiddlewareIfASenderIsNull() null, ))); - $sender->expects($this->once())->method('send')->with($message); + $sender->expects($this->once())->method('send')->with(Envelope::wrap($message)); $next->expects($this->once())->method($this->anything()); $middleware->handle($message, $next); @@ -67,8 +84,7 @@ public function testItCallsTheNextMiddlewareWhenNoSenderForThisMessage() public function testItSkipsReceivedMessages() { - $innerMessage = new DummyMessage('Hey'); - $message = new ReceivedMessage($innerMessage); + $envelope = Envelope::wrap(new DummyMessage('Hey'))->with(new ReceivedMessage()); $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); $next = $this->createPartialMock(\stdClass::class, array('__invoke')); @@ -78,9 +94,9 @@ public function testItSkipsReceivedMessages() ))); $sender->expects($this->never())->method('send'); - $next->expects($this->once())->method('__invoke')->with($innerMessage); + $next->expects($this->once())->method('__invoke')->with($envelope); - $middleware->handle($message, $next); + $middleware->handle($envelope, $next); } } diff --git a/src/Symfony/Component/Messenger/Tests/Asynchronous/Transport/Serialization/SerializerConfigurationTest.php b/src/Symfony/Component/Messenger/Tests/Asynchronous/Transport/Serialization/SerializerConfigurationTest.php new file mode 100644 index 0000000000000..6ebcc8ddfa0ca --- /dev/null +++ b/src/Symfony/Component/Messenger/Tests/Asynchronous/Transport/Serialization/SerializerConfigurationTest.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Tests\Asynchronous\Serialization; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Messenger\Transport\Serialization\SerializerConfiguration; +use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; + +/** + * @author Maxime Steinhausser + */ +class SerializerConfigurationTest extends TestCase +{ + public function testSerialiazable() + { + $config = new SerializerConfiguration(array(ObjectNormalizer::GROUPS => array('Default', 'Extra'))); + + $this->assertTrue(is_subclass_of(SerializerConfiguration::class, \Serializable::class, true)); + $this->assertEquals($config, unserialize(serialize($config))); + } +} diff --git a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php index 37c9aefed4a4d..48217ba731b53 100644 --- a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php +++ b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php @@ -17,6 +17,7 @@ use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ServiceLocator; use Symfony\Component\Messenger\Command\ConsumeMessagesCommand; +use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Transport\AmqpExt\AmqpReceiver; use Symfony\Component\Messenger\Transport\AmqpExt\AmqpSender; use Symfony\Component\Messenger\Handler\Locator\ContainerHandlerLocator; @@ -357,7 +358,7 @@ class DummyReceiver implements ReceiverInterface public function receive(callable $handler): void { for ($i = 0; $i < 3; ++$i) { - $handler(new DummyMessage("Dummy $i")); + $handler(Envelope::wrap(new DummyMessage("Dummy $i"))); } } diff --git a/src/Symfony/Component/Messenger/Tests/EnvelopeTest.php b/src/Symfony/Component/Messenger/Tests/EnvelopeTest.php new file mode 100644 index 0000000000000..053275b6e714c --- /dev/null +++ b/src/Symfony/Component/Messenger/Tests/EnvelopeTest.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Messenger\Asynchronous\Transport\ReceivedMessage; +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\EnvelopeAwareInterface; +use Symfony\Component\Messenger\Middleware\Configuration\ValidationConfiguration; +use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; + +/** + * @author Maxime Steinhausser + */ +class EnvelopeTest extends TestCase +{ + public function testConstruct() + { + $envelope = new Envelope($dummy = new DummyMessage('dummy'), array( + $receivedConfig = new ReceivedMessage(), + )); + + $this->assertSame($dummy, $envelope->getMessage()); + $this->assertArrayHasKey(ReceivedMessage::class, $configs = $envelope->all()); + $this->assertSame($receivedConfig, $configs[ReceivedMessage::class]); + } + + public function testWrap() + { + $first = Envelope::wrap($dummy = new DummyMessage('dummy')); + + $this->assertInstanceOf(Envelope::class, $first); + $this->assertSame($dummy, $first->getMessage()); + + $envelope = Envelope::wrap($first); + $this->assertSame($first, $envelope); + } + + public function testWithReturnsNewInstance() + { + $envelope = Envelope::wrap($dummy = new DummyMessage('dummy')); + + $this->assertNotSame($envelope, $envelope->with(new ReceivedMessage())); + } + + public function testGet() + { + $envelope = Envelope::wrap($dummy = new DummyMessage('dummy')) + ->with($config = new ReceivedMessage()) + ; + + $this->assertSame($config, $envelope->get(ReceivedMessage::class)); + $this->assertNull($envelope->get(ValidationConfiguration::class)); + } + + public function testAll() + { + $envelope = Envelope::wrap($dummy = new DummyMessage('dummy')) + ->with($receivedConfig = new ReceivedMessage()) + ->with($validationConfig = new ValidationConfiguration(array('foo'))) + ; + + $configs = $envelope->all(); + $this->assertArrayHasKey(ReceivedMessage::class, $configs); + $this->assertSame($receivedConfig, $configs[ReceivedMessage::class]); + $this->assertArrayHasKey(ValidationConfiguration::class, $configs); + $this->assertSame($validationConfig, $configs[ValidationConfiguration::class]); + } +} + +class FooConfigurationConsumer implements EnvelopeAwareInterface +{ +} diff --git a/src/Symfony/Component/Messenger/Tests/Middleware/Configuration/ValidationConfigurationTest.php b/src/Symfony/Component/Messenger/Tests/Middleware/Configuration/ValidationConfigurationTest.php new file mode 100644 index 0000000000000..6fd6962994f4f --- /dev/null +++ b/src/Symfony/Component/Messenger/Tests/Middleware/Configuration/ValidationConfigurationTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Tests\Middleware\Configuration; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Messenger\Middleware\Configuration\ValidationConfiguration; +use Symfony\Component\Validator\Constraints\GroupSequence; + +/** + * @author Maxime Steinhausser + */ +class ValidationConfigurationTest extends TestCase +{ + public function testConfig() + { + $config = new ValidationConfiguration($groups = array('Default', 'Extra')); + $this->assertSame($groups, $config->getGroups()); + + $config = new ValidationConfiguration($groups = new GroupSequence(array('Default', 'Then'))); + $this->assertSame($groups, $config->getGroups()); + } + + public function testSerialiazable() + { + $this->assertTrue(is_subclass_of(ValidationConfiguration::class, \Serializable::class, true)); + $this->assertEquals($config = new ValidationConfiguration(array('Default', 'Extra')), unserialize(serialize($config))); + $this->assertEquals($config = new ValidationConfiguration(new GroupSequence(array('Default', 'Then'))), unserialize(serialize($config))); + } +} diff --git a/src/Symfony/Component/Messenger/Tests/Middleware/ValidationMiddlewareTest.php b/src/Symfony/Component/Messenger/Tests/Middleware/ValidationMiddlewareTest.php index 2bd2ba8af22e4..b2a44b5fa6723 100644 --- a/src/Symfony/Component/Messenger/Tests/Middleware/ValidationMiddlewareTest.php +++ b/src/Symfony/Component/Messenger/Tests/Middleware/ValidationMiddlewareTest.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Messenger\Tests\Middleware; use PHPUnit\Framework\TestCase; +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Middleware\Configuration\ValidationConfiguration; use Symfony\Component\Messenger\Middleware\ValidationMiddleware; use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; use Symfony\Component\Validator\ConstraintViolationListInterface; @@ -43,6 +45,30 @@ public function testValidateAndNextMiddleware() $this->assertSame('Hello', $result); } + public function testValidateWithConfigurationAndNextMiddleware() + { + $envelope = Envelope::wrap($message = new DummyMessage('Hey'))->with(new ValidationConfiguration($groups = array('Default', 'Extra'))); + + $validator = $this->createMock(ValidatorInterface::class); + $validator + ->expects($this->once()) + ->method('validate') + ->with($message, null, $groups) + ->willReturn($this->createMock(ConstraintViolationListInterface::class)) + ; + $next = $this->createPartialMock(\stdClass::class, array('__invoke')); + $next + ->expects($this->once()) + ->method('__invoke') + ->with($envelope) + ->willReturn('Hello') + ; + + $result = (new ValidationMiddleware($validator))->handle($envelope, $next); + + $this->assertSame('Hello', $result); + } + /** * @expectedException \Symfony\Component\Messenger\Exception\ValidationFailedException * @expectedExceptionMessage Message of type "Symfony\Component\Messenger\Tests\Fixtures\DummyMessage" failed validation. diff --git a/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpExtIntegrationTest.php b/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpExtIntegrationTest.php index 5ce9c457e9841..c01ffdadb7f0a 100644 --- a/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpExtIntegrationTest.php +++ b/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpExtIntegrationTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Messenger\Tests\Transport\AmqpExt; use PHPUnit\Framework\TestCase; +use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Transport\AmqpExt\AmqpReceiver; use Symfony\Component\Messenger\Transport\AmqpExt\AmqpSender; use Symfony\Component\Messenger\Transport\AmqpExt\Connection; @@ -50,12 +51,12 @@ public function testItSendsAndReceivesMessages() $sender = new AmqpSender($serializer, $connection); $receiver = new AmqpReceiver($serializer, $connection); - $sender->send($firstMessage = new DummyMessage('First')); - $sender->send($secondMessage = new DummyMessage('Second')); + $sender->send($first = Envelope::wrap(new DummyMessage('First'))); + $sender->send($second = Envelope::wrap(new DummyMessage('Second'))); $receivedMessages = 0; - $receiver->receive(function ($message) use ($receiver, &$receivedMessages, $firstMessage, $secondMessage) { - $this->assertEquals(0 == $receivedMessages ? $firstMessage : $secondMessage, $message); + $receiver->receive(function (?Envelope $envelope) use ($receiver, &$receivedMessages, $first, $second) { + $this->assertEquals(0 == $receivedMessages ? $first : $second, $envelope); if (2 === ++$receivedMessages) { $receiver->stop(); @@ -74,7 +75,7 @@ public function testItReceivesSignals() $connection->queue()->purge(); $sender = new AmqpSender($serializer, $connection); - $sender->send(new DummyMessage('Hello')); + $sender->send(Envelope::wrap(new DummyMessage('Hello'))); $amqpReadTimeout = 30; $dsn = getenv('MESSENGER_AMQP_DSN').'?read_timeout='.$amqpReadTimeout; @@ -98,7 +99,15 @@ public function testItReceivesSignals() $this->assertFalse($process->isRunning()); $this->assertLessThan($amqpReadTimeout, microtime(true) - $signalTime); - $this->assertEquals($expectedOutput."Get message: Symfony\Component\Messenger\Asynchronous\Transport\ReceivedMessage\nDone.\n", $process->getOutput()); + $this->assertSame($expectedOutput.<<<'TXT' +Get envelope with message: Symfony\Component\Messenger\Tests\Fixtures\DummyMessage +with items: [ + "Symfony\\Component\\Messenger\\Asynchronous\\Transport\\ReceivedMessage" +] +Done. + +TXT + , $process->getOutput()); } /** @@ -114,12 +123,11 @@ public function testItSupportsTimeoutAndTicksNullMessagesToTheHandler() $connection->setup(); $connection->queue()->purge(); - $sender = new AmqpSender($serializer, $connection); $receiver = new AmqpReceiver($serializer, $connection); $receivedMessages = 0; - $receiver->receive(function ($message) use ($receiver, &$receivedMessages) { - $this->assertNull($message); + $receiver->receive(function (?Envelope $envelope) use ($receiver, &$receivedMessages) { + $this->assertNull($envelope); if (2 === ++$receivedMessages) { $receiver->stop(); diff --git a/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpReceiverTest.php b/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpReceiverTest.php index 2045764216ca3..4d0b779e3b3f4 100644 --- a/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpReceiverTest.php +++ b/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpReceiverTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Messenger\Tests\Transport\AmqpExt; use PHPUnit\Framework\TestCase; +use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Transport\AmqpExt\AmqpReceiver; use Symfony\Component\Messenger\Transport\AmqpExt\Connection; use Symfony\Component\Messenger\Transport\AmqpExt\Exception\RejectMessageExceptionInterface; @@ -44,8 +45,8 @@ public function testItSendTheDecodedMessageToTheHandlerAndAcknowledgeIt() $connection->expects($this->once())->method('ack')->with($envelope); $receiver = new AmqpReceiver($serializer, $connection); - $receiver->receive(function ($message) use ($receiver) { - $this->assertEquals(new DummyMessage('Hi'), $message); + $receiver->receive(function (?Envelope $envelope) use ($receiver) { + $this->assertEquals(new DummyMessage('Hi'), $envelope->getMessage()); $receiver->stop(); }); } diff --git a/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpSenderTest.php b/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpSenderTest.php index 54859a58acf1b..8848b022f6aad 100644 --- a/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpSenderTest.php +++ b/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpSenderTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Messenger\Tests\Transport\AmqpExt; +use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Transport\AmqpExt\AmqpSender; use Symfony\Component\Messenger\Transport\AmqpExt\Connection; use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; @@ -24,16 +25,16 @@ class AmqpSenderTest extends TestCase { public function testItSendsTheEncodedMessage() { - $message = new DummyMessage('Oy'); + $envelope = Envelope::wrap(new DummyMessage('Oy')); $encoded = array('body' => '...', 'headers' => array('type' => DummyMessage::class)); $encoder = $this->getMockBuilder(EncoderInterface::class)->getMock(); - $encoder->method('encode')->with($message)->willReturnOnConsecutiveCalls($encoded); + $encoder->method('encode')->with($envelope)->willReturnOnConsecutiveCalls($encoded); $connection = $this->getMockBuilder(Connection::class)->disableOriginalConstructor()->getMock(); $connection->expects($this->once())->method('publish')->with($encoded['body'], $encoded['headers']); $sender = new AmqpSender($encoder, $connection); - $sender->send($message); + $sender->send($envelope); } } diff --git a/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/Fixtures/long_receiver.php b/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/Fixtures/long_receiver.php index 2115b1a81dc23..b7231a5d47fac 100644 --- a/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/Fixtures/long_receiver.php +++ b/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/Fixtures/long_receiver.php @@ -12,10 +12,9 @@ require_once $autoload; +use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Messenger\Transport\AmqpExt\AmqpReceiver; -use Symfony\Component\Messenger\Transport\AmqpExt\AmqpSender; use Symfony\Component\Messenger\Transport\AmqpExt\Connection; -use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Messenger\Transport\Serialization\Serializer; use Symfony\Component\Messenger\Worker; use Symfony\Component\Serializer as SerializerComponent; @@ -27,13 +26,14 @@ ); $connection = Connection::fromDsn(getenv('DSN')); -$sender = new AmqpSender($serializer, $connection); $receiver = new AmqpReceiver($serializer, $connection); $worker = new Worker($receiver, new class() implements MessageBusInterface { - public function dispatch($message) + public function dispatch($envelope) { - echo 'Get message: '.get_class($message)."\n"; + echo 'Get envelope with message: '.get_class($envelope->getMessage())."\n"; + echo sprintf("with items: %s\n", json_encode(array_keys($envelope->all()), JSON_PRETTY_PRINT)); + sleep(30); echo "Done.\n"; } diff --git a/src/Symfony/Component/Messenger/Tests/Transport/Enhancers/StopWhenMemoryUsageIsExceededReceiverTest.php b/src/Symfony/Component/Messenger/Tests/Transport/Enhancers/StopWhenMemoryUsageIsExceededReceiverTest.php index f317c6a5bfe94..a34be3bfc2ce5 100644 --- a/src/Symfony/Component/Messenger/Tests/Transport/Enhancers/StopWhenMemoryUsageIsExceededReceiverTest.php +++ b/src/Symfony/Component/Messenger/Tests/Transport/Enhancers/StopWhenMemoryUsageIsExceededReceiverTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; +use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Tests\Fixtures\CallbackReceiver; use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; use Symfony\Component\Messenger\Transport\Enhancers\StopWhenMemoryUsageIsExceededReceiver; @@ -25,7 +26,7 @@ class StopWhenMemoryUsageIsExceededReceiverTest extends TestCase public function testReceiverStopsWhenMemoryLimitExceeded(int $memoryUsage, int $memoryLimit, bool $shouldStop) { $callable = function ($handler) { - $handler(new DummyMessage('API')); + $handler(Envelope::wrap(new DummyMessage('API'))); }; $decoratedReceiver = $this->getMockBuilder(CallbackReceiver::class) @@ -58,7 +59,7 @@ public function memoryProvider() public function testReceiverLogsMemoryExceededWhenLoggerIsGiven() { $callable = function ($handler) { - $handler(new DummyMessage('API')); + $handler(Envelope::wrap(new DummyMessage('API'))); }; $decoratedReceiver = $this->getMockBuilder(CallbackReceiver::class) diff --git a/src/Symfony/Component/Messenger/Tests/Transport/Enhancers/StopWhenMessageCountIsExceededReceiverTest.php b/src/Symfony/Component/Messenger/Tests/Transport/Enhancers/StopWhenMessageCountIsExceededReceiverTest.php index 7a516744e1893..e5c51335b3b8e 100644 --- a/src/Symfony/Component/Messenger/Tests/Transport/Enhancers/StopWhenMessageCountIsExceededReceiverTest.php +++ b/src/Symfony/Component/Messenger/Tests/Transport/Enhancers/StopWhenMessageCountIsExceededReceiverTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; +use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Tests\Fixtures\CallbackReceiver; use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; use Symfony\Component\Messenger\Transport\Enhancers\StopWhenMessageCountIsExceededReceiver; @@ -25,9 +26,9 @@ class StopWhenMessageCountIsExceededReceiverTest extends TestCase public function testReceiverStopsWhenMaximumCountExceeded($max, $shouldStop) { $callable = function ($handler) { - $handler(new DummyMessage('First message')); - $handler(new DummyMessage('Second message')); - $handler(new DummyMessage('Third message')); + $handler(Envelope::wrap(new DummyMessage('First message'))); + $handler(Envelope::wrap(new DummyMessage('Second message'))); + $handler(Envelope::wrap(new DummyMessage('Third message'))); }; $decoratedReceiver = $this->getMockBuilder(CallbackReceiver::class) @@ -78,7 +79,7 @@ public function testReceiverDoesntIncreaseItsCounterWhenReceiveNullMessage() public function testReceiverLogsMaximumCountExceededWhenLoggerIsGiven() { $callable = function ($handler) { - $handler(new DummyMessage('First message')); + $handler(Envelope::wrap(new DummyMessage('First message'))); }; $decoratedReceiver = $this->getMockBuilder(CallbackReceiver::class) diff --git a/src/Symfony/Component/Messenger/Tests/Transport/Serialization/SerializerTest.php b/src/Symfony/Component/Messenger/Tests/Transport/Serialization/SerializerTest.php index 2e227c0f2f717..214b773092e8c 100644 --- a/src/Symfony/Component/Messenger/Tests/Transport/Serialization/SerializerTest.php +++ b/src/Symfony/Component/Messenger/Tests/Transport/Serialization/SerializerTest.php @@ -12,8 +12,11 @@ namespace Symfony\Component\Messenger\Tests\Transport\Serialization; use PHPUnit\Framework\TestCase; +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Middleware\Configuration\ValidationConfiguration; use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; use Symfony\Component\Messenger\Transport\Serialization\Serializer; +use Symfony\Component\Messenger\Transport\Serialization\SerializerConfiguration; use Symfony\Component\Serializer as SerializerComponent; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; @@ -26,9 +29,23 @@ public function testEncodedIsDecodable() new SerializerComponent\Serializer(array(new ObjectNormalizer()), array('json' => new JsonEncoder())) ); - $message = new DummyMessage('Hello'); + $envelope = Envelope::wrap(new DummyMessage('Hello')); - $this->assertEquals($message, $serializer->decode($serializer->encode($message))); + $this->assertEquals($envelope, $serializer->decode($serializer->encode($envelope))); + } + + public function testEncodedWithConfigurationIsDecodable() + { + $serializer = new Serializer( + new SerializerComponent\Serializer(array(new ObjectNormalizer()), array('json' => new JsonEncoder())) + ); + + $envelope = Envelope::wrap(new DummyMessage('Hello')) + ->with(new SerializerConfiguration(array(ObjectNormalizer::GROUPS => array('foo')))) + ->with(new ValidationConfiguration(array('foo', 'bar'))) + ; + + $this->assertEquals($envelope, $serializer->decode($serializer->encode($envelope))); } public function testEncodedIsHavingTheBodyAndTypeHeader() @@ -37,11 +54,12 @@ public function testEncodedIsHavingTheBodyAndTypeHeader() new SerializerComponent\Serializer(array(new ObjectNormalizer()), array('json' => new JsonEncoder())) ); - $encoded = $serializer->encode(new DummyMessage('Hello')); + $encoded = $serializer->encode(Envelope::wrap(new DummyMessage('Hello'))); $this->assertArrayHasKey('body', $encoded); $this->assertArrayHasKey('headers', $encoded); $this->assertArrayHasKey('type', $encoded['headers']); + $this->assertArrayNotHasKey('X-Message-Envelope-Items', $encoded['headers']); $this->assertEquals(DummyMessage::class, $encoded['headers']['type']); } @@ -55,10 +73,31 @@ public function testUsesTheCustomFormatAndContext() $encoder = new Serializer($serializer, 'csv', array('foo' => 'bar')); - $encoded = $encoder->encode($message); + $encoded = $encoder->encode(Envelope::wrap($message)); $decoded = $encoder->decode($encoded); $this->assertSame('Yay', $encoded['body']); - $this->assertSame($message, $decoded); + $this->assertSame($message, $decoded->getMessage()); + } + + public function testEncodedWithSerializationConfiguration() + { + $serializer = new Serializer( + new SerializerComponent\Serializer(array(new ObjectNormalizer()), array('json' => new JsonEncoder())) + ); + + $envelope = Envelope::wrap(new DummyMessage('Hello')) + ->with(new SerializerConfiguration(array(ObjectNormalizer::GROUPS => array('foo')))) + ->with(new ValidationConfiguration(array('foo', 'bar'))) + ; + + $encoded = $serializer->encode($envelope); + + $this->assertArrayHasKey('body', $encoded); + $this->assertArrayHasKey('headers', $encoded); + $this->assertArrayHasKey('type', $encoded['headers']); + $this->assertEquals(DummyMessage::class, $encoded['headers']['type']); + $this->assertArrayHasKey('X-Message-Envelope-Items', $encoded['headers']); + $this->assertSame('a:2:{s:75:"Symfony\Component\Messenger\Transport\Serialization\SerializerConfiguration";C:75:"Symfony\Component\Messenger\Transport\Serialization\SerializerConfiguration":59:{a:1:{s:7:"context";a:1:{s:6:"groups";a:1:{i:0;s:3:"foo";}}}}s:76:"Symfony\Component\Messenger\Middleware\Configuration\ValidationConfiguration";C:76:"Symfony\Component\Messenger\Middleware\Configuration\ValidationConfiguration":82:{a:2:{s:6:"groups";a:2:{i:0;s:3:"foo";i:1;s:3:"bar";}s:17:"is_group_sequence";b:0;}}}', $encoded['headers']['X-Message-Envelope-Items']); } } diff --git a/src/Symfony/Component/Messenger/Tests/WorkerTest.php b/src/Symfony/Component/Messenger/Tests/WorkerTest.php index 7599d8dadc221..bdfa6fe188d62 100644 --- a/src/Symfony/Component/Messenger/Tests/WorkerTest.php +++ b/src/Symfony/Component/Messenger/Tests/WorkerTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Messenger\Asynchronous\Transport\ReceivedMessage; +use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Messenger\Tests\Fixtures\CallbackReceiver; use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; @@ -22,29 +23,33 @@ class WorkerTest extends TestCase { public function testWorkerDispatchTheReceivedMessage() { - $receiver = new CallbackReceiver(function ($handler) { - $handler(new DummyMessage('API')); - $handler(new DummyMessage('IPA')); + $apiMessage = new DummyMessage('API'); + $ipaMessage = new DummyMessage('IPA'); + + $receiver = new CallbackReceiver(function ($handler) use ($apiMessage, $ipaMessage) { + $handler(Envelope::wrap($apiMessage)); + $handler(Envelope::wrap($ipaMessage)); }); $bus = $this->getMockBuilder(MessageBusInterface::class)->getMock(); - $bus->expects($this->at(0))->method('dispatch')->with(new ReceivedMessage(new DummyMessage('API'))); - $bus->expects($this->at(1))->method('dispatch')->with(new ReceivedMessage(new DummyMessage('IPA'))); + $bus->expects($this->at(0))->method('dispatch')->with(Envelope::wrap($apiMessage)->with(new ReceivedMessage())); + $bus->expects($this->at(1))->method('dispatch')->with(Envelope::wrap($ipaMessage)->with(new ReceivedMessage())); $worker = new Worker($receiver, $bus); $worker->run(); } - public function testWorkerDoesNotWrapMessagesAlreadyWrappedInReceivedMessages() + public function testWorkerDoesNotWrapMessagesAlreadyWrappedWithReceivedMessage() { - $receiver = new CallbackReceiver(function ($handler) { - $handler(new ReceivedMessage(new DummyMessage('API'))); + $envelop = Envelope::wrap(new DummyMessage('API'))->with(new ReceivedMessage()); + $receiver = new CallbackReceiver(function ($handler) use ($envelop) { + $handler($envelop); }); $bus = $this->getMockBuilder(MessageBusInterface::class)->getMock(); - $bus->expects($this->at(0))->method('dispatch')->with(new ReceivedMessage(new DummyMessage('API'))); + $bus->expects($this->at(0))->method('dispatch')->with($envelop); $worker = new Worker($receiver, $bus); $worker->run(); @@ -54,7 +59,7 @@ public function testWorkerIsThrowingExceptionsBackToGenerators() { $receiver = new CallbackReceiver(function ($handler) { try { - $handler(new DummyMessage('Hello')); + $handler(Envelope::wrap(new DummyMessage('Hello'))); $this->assertTrue(false, 'This should not be called because the exception is sent back to the generator.'); } catch (\InvalidArgumentException $e) { diff --git a/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpReceiver.php b/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpReceiver.php index e61ef0389531e..0e6fbff8ee340 100644 --- a/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpReceiver.php +++ b/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpReceiver.php @@ -22,13 +22,13 @@ */ class AmqpReceiver implements ReceiverInterface { - private $messageDecoder; + private $decoder; private $connection; private $shouldStop; - public function __construct(DecoderInterface $messageDecoder, Connection $connection) + public function __construct(DecoderInterface $decoder, Connection $connection) { - $this->messageDecoder = $messageDecoder; + $this->decoder = $decoder; $this->connection = $connection; } @@ -38,8 +38,8 @@ public function __construct(DecoderInterface $messageDecoder, Connection $connec public function receive(callable $handler): void { while (!$this->shouldStop) { - $message = $this->connection->get(); - if (null === $message) { + $AMQPEnvelope = $this->connection->get(); + if (null === $AMQPEnvelope) { $handler(null); usleep($this->connection->getConnectionCredentials()['loop_sleep'] ?? 200000); @@ -51,18 +51,18 @@ public function receive(callable $handler): void } try { - $handler($this->messageDecoder->decode(array( - 'body' => $message->getBody(), - 'headers' => $message->getHeaders(), + $handler($this->decoder->decode(array( + 'body' => $AMQPEnvelope->getBody(), + 'headers' => $AMQPEnvelope->getHeaders(), ))); - $this->connection->ack($message); + $this->connection->ack($AMQPEnvelope); } catch (RejectMessageExceptionInterface $e) { - $this->connection->reject($message); + $this->connection->reject($AMQPEnvelope); throw $e; } catch (\Throwable $e) { - $this->connection->nack($message, AMQP_REQUEUE); + $this->connection->nack($AMQPEnvelope, AMQP_REQUEUE); throw $e; } finally { diff --git a/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpSender.php b/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpSender.php index 523c35db0f11b..397c52dec2a20 100644 --- a/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpSender.php +++ b/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpSender.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Messenger\Transport\AmqpExt; +use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Transport\SenderInterface; use Symfony\Component\Messenger\Transport\Serialization\EncoderInterface; @@ -21,21 +22,21 @@ */ class AmqpSender implements SenderInterface { - private $messageEncoder; + private $encoder; private $connection; - public function __construct(EncoderInterface $messageEncoder, Connection $connection) + public function __construct(EncoderInterface $encoder, Connection $connection) { - $this->messageEncoder = $messageEncoder; + $this->encoder = $encoder; $this->connection = $connection; } /** * {@inheritdoc} */ - public function send($message): void + public function send(Envelope $envelope) { - $encodedMessage = $this->messageEncoder->encode($message); + $encodedMessage = $this->encoder->encode($envelope); $this->connection->publish($encodedMessage['body'], $encodedMessage['headers']); } diff --git a/src/Symfony/Component/Messenger/Transport/Enhancers/StopWhenMemoryUsageIsExceededReceiver.php b/src/Symfony/Component/Messenger/Transport/Enhancers/StopWhenMemoryUsageIsExceededReceiver.php index 4a05afe7707fb..37b1978233e9f 100644 --- a/src/Symfony/Component/Messenger/Transport/Enhancers/StopWhenMemoryUsageIsExceededReceiver.php +++ b/src/Symfony/Component/Messenger/Transport/Enhancers/StopWhenMemoryUsageIsExceededReceiver.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Messenger\Transport\Enhancers; use Psr\Log\LoggerInterface; +use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Transport\ReceiverInterface; /** @@ -36,8 +37,8 @@ public function __construct(ReceiverInterface $decoratedReceiver, int $memoryLim public function receive(callable $handler): void { - $this->decoratedReceiver->receive(function ($message) use ($handler) { - $handler($message); + $this->decoratedReceiver->receive(function (?Envelope $envelope) use ($handler) { + $handler($envelope); $memoryResolver = $this->memoryResolver; if ($memoryResolver() > $this->memoryLimit) { diff --git a/src/Symfony/Component/Messenger/Transport/Enhancers/StopWhenMessageCountIsExceededReceiver.php b/src/Symfony/Component/Messenger/Transport/Enhancers/StopWhenMessageCountIsExceededReceiver.php index dc61466bbf401..420eb8fa63a40 100644 --- a/src/Symfony/Component/Messenger/Transport/Enhancers/StopWhenMessageCountIsExceededReceiver.php +++ b/src/Symfony/Component/Messenger/Transport/Enhancers/StopWhenMessageCountIsExceededReceiver.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Messenger\Transport\Enhancers; use Psr\Log\LoggerInterface; +use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Transport\ReceiverInterface; /** @@ -34,10 +35,10 @@ public function receive(callable $handler): void { $receivedMessages = 0; - $this->decoratedReceiver->receive(function ($message) use ($handler, &$receivedMessages) { - $handler($message); + $this->decoratedReceiver->receive(function (?Envelope $envelope) use ($handler, &$receivedMessages) { + $handler($envelope); - if (null !== $message && ++$receivedMessages >= $this->maximumNumberOfMessages) { + if (null !== $envelope && ++$receivedMessages >= $this->maximumNumberOfMessages) { $this->stop(); if (null !== $this->logger) { $this->logger->info('Receiver stopped due to maximum count of {count} exceeded', array('count' => $this->maximumNumberOfMessages)); diff --git a/src/Symfony/Component/Messenger/Transport/ReceiverInterface.php b/src/Symfony/Component/Messenger/Transport/ReceiverInterface.php index 1c29fbe43abe7..0f61fe3428d7e 100644 --- a/src/Symfony/Component/Messenger/Transport/ReceiverInterface.php +++ b/src/Symfony/Component/Messenger/Transport/ReceiverInterface.php @@ -21,10 +21,10 @@ interface ReceiverInterface /** * Receive some messages to the given handler. * - * The handler will have, as argument, the received message. Note that this message - * can be `null` if the timeout to receive something has expired. + * The handler will have, as argument, the received {@link \Symfony\Component\Messenger\Envelope} containing the message. + * Note that this envelope can be `null` if the timeout to receive something has expired. */ - public function receive(callable $handler) : void; + public function receive(callable $handler): void; /** * Stop receiving some messages. diff --git a/src/Symfony/Component/Messenger/Transport/SenderInterface.php b/src/Symfony/Component/Messenger/Transport/SenderInterface.php index 93b1bd7cbaa3f..ba36b80fe2af2 100644 --- a/src/Symfony/Component/Messenger/Transport/SenderInterface.php +++ b/src/Symfony/Component/Messenger/Transport/SenderInterface.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Messenger\Transport; +use Symfony\Component\Messenger\Envelope; + /** * @author Samuel Roze * @@ -19,9 +21,9 @@ interface SenderInterface { /** - * Sends the given message. + * Sends the given envelope. * - * @param object $message + * @param Envelope $envelope */ - public function send($message): void; + public function send(Envelope $envelope); } diff --git a/src/Symfony/Component/Messenger/Transport/Serialization/DecoderInterface.php b/src/Symfony/Component/Messenger/Transport/Serialization/DecoderInterface.php index a232dfbf10e30..026a32103316b 100644 --- a/src/Symfony/Component/Messenger/Transport/Serialization/DecoderInterface.php +++ b/src/Symfony/Component/Messenger/Transport/Serialization/DecoderInterface.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Messenger\Transport\Serialization; +use Symfony\Component\Messenger\Envelope; + /** * @author Samuel Roze * @@ -19,16 +21,16 @@ interface DecoderInterface { /** - * Decodes the message from an encoded-form. + * Decodes an envelope and its message from an encoded-form. * - * The `$encodedMessage` parameter is a key-value array that - * describes the message, that will be used by the different transports. + * The `$encodedEnvelope` parameter is a key-value array that + * describes the envelope and its content, that will be used by the different transports. * * The most common keys are: * - `body` (string) - the message body * - `headers` (string) - a key/value pair of headers * - * @return object + * @return Envelope */ - public function decode(array $encodedMessage); + public function decode(array $encodedEnvelope): Envelope; } diff --git a/src/Symfony/Component/Messenger/Transport/Serialization/EncoderInterface.php b/src/Symfony/Component/Messenger/Transport/Serialization/EncoderInterface.php index 658b9e5b5e681..1dce6fdae33cd 100644 --- a/src/Symfony/Component/Messenger/Transport/Serialization/EncoderInterface.php +++ b/src/Symfony/Component/Messenger/Transport/Serialization/EncoderInterface.php @@ -10,6 +10,7 @@ */ namespace Symfony\Component\Messenger\Transport\Serialization; +use Symfony\Component\Messenger\Envelope; /** * @author Samuel Roze @@ -19,14 +20,14 @@ interface EncoderInterface { /** - * Encodes a message to a common format understandable by transports. The encoded array should only - * contain scalar and arrays. + * Encodes an envelope content (message & items) to a common format understandable by transports. + * The encoded array should only contain scalar and arrays. * * The most common keys of the encoded array are: * - `body` (string) - the message body * - `headers` (string) - a key/value pair of headers * - * @param object $message The object that is put on the MessageBus by the user + * @param Envelope $envelope The envelop containing the message put on the MessageBus by the user */ - public function encode($message): array; + public function encode(Envelope $envelope): array; } diff --git a/src/Symfony/Component/Messenger/Transport/Serialization/Serializer.php b/src/Symfony/Component/Messenger/Transport/Serialization/Serializer.php index 65c2ac55e8886..cfb30090e5dfa 100644 --- a/src/Symfony/Component/Messenger/Transport/Serialization/Serializer.php +++ b/src/Symfony/Component/Messenger/Transport/Serialization/Serializer.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Messenger\Transport\Serialization; +use Symfony\Component\Messenger\Envelope; use Symfony\Component\Serializer\SerializerInterface; /** @@ -32,27 +33,48 @@ public function __construct(SerializerInterface $serializer, string $format = 'j /** * {@inheritdoc} */ - public function decode(array $encodedMessage) + public function decode(array $encodedEnvelope): Envelope { - if (empty($encodedMessage['body']) || empty($encodedMessage['headers'])) { - throw new \InvalidArgumentException('Encoded message should have at least a `body` and some `headers`.'); + if (empty($encodedEnvelope['body']) || empty($encodedEnvelope['headers'])) { + throw new \InvalidArgumentException('Encoded envelope should have at least a `body` and some `headers`.'); } - if (empty($encodedMessage['headers']['type'])) { - throw new \InvalidArgumentException('Encoded message does not have a `type` header.'); + if (empty($encodedEnvelope['headers']['type'])) { + throw new \InvalidArgumentException('Encoded envelope does not have a `type` header.'); } - return $this->serializer->deserialize($encodedMessage['body'], $encodedMessage['headers']['type'], $this->format, $this->context); + $envelopeItems = isset($encodedEnvelope['headers']['X-Message-Envelope-Items']) ? unserialize($encodedEnvelope['headers']['X-Message-Envelope-Items']) : array(); + + $context = $this->context; + /** @var SerializerConfiguration|null $serializerConfig */ + if ($serializerConfig = $envelopeItems[SerializerConfiguration::class] ?? null) { + $context = $serializerConfig->getContext() + $context; + } + + $message = $this->serializer->deserialize($encodedEnvelope['body'], $encodedEnvelope['headers']['type'], $this->format, $context); + + return new Envelope($message, $envelopeItems); } /** * {@inheritdoc} */ - public function encode($message): array + public function encode(Envelope $envelope): array { + $context = $this->context; + /** @var SerializerConfiguration|null $serializerConfig */ + if ($serializerConfig = $envelope->get(SerializerConfiguration::class)) { + $context = $serializerConfig->getContext() + $context; + } + + $headers = array('type' => \get_class($envelope->getMessage())); + if ($configurations = $envelope->all()) { + $headers['X-Message-Envelope-Items'] = serialize($configurations); + } + return array( - 'body' => $this->serializer->serialize($message, $this->format, $this->context), - 'headers' => array('type' => \get_class($message)), + 'body' => $this->serializer->serialize($envelope->getMessage(), $this->format, $context), + 'headers' => $headers, ); } } diff --git a/src/Symfony/Component/Messenger/Transport/Serialization/SerializerConfiguration.php b/src/Symfony/Component/Messenger/Transport/Serialization/SerializerConfiguration.php new file mode 100644 index 0000000000000..478e197080adf --- /dev/null +++ b/src/Symfony/Component/Messenger/Transport/Serialization/SerializerConfiguration.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Transport\Serialization; + +use Symfony\Component\Messenger\EnvelopeItemInterface; + +/** + * @author Maxime Steinhausser + * + * @experimental in 4.1 + */ +final class SerializerConfiguration implements EnvelopeItemInterface +{ + private $context; + + public function __construct(array $context) + { + $this->context = $context; + } + + public function getContext(): array + { + return $this->context; + } + + public function serialize() + { + return serialize(array('context' => $this->context)); + } + + public function unserialize($serialized) + { + list('context' => $context) = unserialize($serialized, array('allowed_classes' => false)); + + $this->__construct($context); + } +} diff --git a/src/Symfony/Component/Messenger/Worker.php b/src/Symfony/Component/Messenger/Worker.php index 2033f3a770fd1..918b61a52a18c 100644 --- a/src/Symfony/Component/Messenger/Worker.php +++ b/src/Symfony/Component/Messenger/Worker.php @@ -41,16 +41,12 @@ public function run() }); } - $this->receiver->receive(function ($message) { - if (null === $message) { + $this->receiver->receive(function (?Envelope $envelope) { + if (null === $envelope) { return; } - if (!$message instanceof ReceivedMessage) { - $message = new ReceivedMessage($message); - } - - $this->bus->dispatch($message); + $this->bus->dispatch($envelope->with(new ReceivedMessage())); }); } } From 599f32c0852242d782cf491d7d78ad586e8af5f3 Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Mon, 7 May 2018 16:54:04 +0100 Subject: [PATCH 19/76] Ensure the envelope is passed back and can be altered Ensure that the middlewares can also update the message within the envelope --- src/Symfony/Component/Messenger/Envelope.php | 9 ++ .../Component/Messenger/MessageBus.php | 15 ++- .../Messenger/Tests/MessageBusTest.php | 117 ++++++++++++++++++ 3 files changed, 136 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Messenger/Envelope.php b/src/Symfony/Component/Messenger/Envelope.php index 14778593c4baf..4d5f7a02a9d5d 100644 --- a/src/Symfony/Component/Messenger/Envelope.php +++ b/src/Symfony/Component/Messenger/Envelope.php @@ -57,6 +57,15 @@ public function with(EnvelopeItemInterface $item): self return $cloned; } + public function withMessage($message): self + { + $cloned = clone $this; + + $cloned->message = $message; + + return $cloned; + } + public function get(string $itemFqcn): ?EnvelopeItemInterface { return $this->items[$itemFqcn] ?? null; diff --git a/src/Symfony/Component/Messenger/MessageBus.php b/src/Symfony/Component/Messenger/MessageBus.php index c467a6f79dacb..89fa8e04fa23c 100644 --- a/src/Symfony/Component/Messenger/MessageBus.php +++ b/src/Symfony/Component/Messenger/MessageBus.php @@ -44,10 +44,10 @@ public function dispatch($message) throw new InvalidArgumentException(sprintf('Invalid type for message argument. Expected object, but got "%s".', \gettype($message))); } - return \call_user_func($this->callableForNextMiddleware(0), $message); + return \call_user_func($this->callableForNextMiddleware(0, Envelope::wrap($message)), $message); } - private function callableForNextMiddleware(int $index): callable + private function callableForNextMiddleware(int $index, Envelope $currentEnvelope): callable { if (null === $this->indexedMiddlewareHandlers) { $this->indexedMiddlewareHandlers = \is_array($this->middlewareHandlers) ? array_values($this->middlewareHandlers) : iterator_to_array($this->middlewareHandlers, false); @@ -59,14 +59,19 @@ private function callableForNextMiddleware(int $index): callable $middleware = $this->indexedMiddlewareHandlers[$index]; - return function ($message) use ($middleware, $index) { - $message = Envelope::wrap($message); + return function ($message) use ($middleware, $index, $currentEnvelope) { + if ($message instanceof Envelope) { + $currentEnvelope = $message; + } else { + $message = $currentEnvelope->withMessage($message); + } + if (!$middleware instanceof EnvelopeAwareInterface) { // Do not provide the envelope if the middleware cannot read it: $message = $message->getMessage(); } - return $middleware->handle($message, $this->callableForNextMiddleware($index + 1)); + return $middleware->handle($message, $this->callableForNextMiddleware($index + 1, $currentEnvelope)); }; } } diff --git a/src/Symfony/Component/Messenger/Tests/MessageBusTest.php b/src/Symfony/Component/Messenger/Tests/MessageBusTest.php index 40eecf9c54e43..ba40eb3f9a12c 100644 --- a/src/Symfony/Component/Messenger/Tests/MessageBusTest.php +++ b/src/Symfony/Component/Messenger/Tests/MessageBusTest.php @@ -12,6 +12,10 @@ namespace Symfony\Component\Messenger\Tests; use PHPUnit\Framework\TestCase; +use Symfony\Component\Messenger\Asynchronous\Transport\ReceivedMessage; +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\EnvelopeAwareInterface; +use Symfony\Component\Messenger\EnvelopeItemInterface; use Symfony\Component\Messenger\MessageBus; use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Messenger\Middleware\MiddlewareInterface; @@ -61,4 +65,117 @@ public function testItCallsMiddlewareAndChainTheReturnValue() $this->assertEquals($responseFromDepthMiddleware, $bus->dispatch($message)); } + + public function testItKeepsTheEnvelopeEvenThroughAMiddlewareThatIsNotEnvelopeAware() + { + $message = new DummyMessage('Hello'); + $envelope = new Envelope($message, array(new ReceivedMessage())); + + $firstMiddleware = $this->getMockBuilder(MiddlewareInterface::class)->getMock(); + $firstMiddleware->expects($this->once()) + ->method('handle') + ->with($message, $this->anything()) + ->will($this->returnCallback(function ($message, $next) { + return $next($message); + })); + + $secondMiddleware = $this->getMockBuilder(array(MiddlewareInterface::class, EnvelopeAwareInterface::class))->getMock(); + $secondMiddleware->expects($this->once()) + ->method('handle') + ->with($envelope, $this->anything()) + ; + + $bus = new MessageBus(array( + $firstMiddleware, + $secondMiddleware, + )); + + $bus->dispatch($envelope); + } + + public function testThatAMiddlewareCanAddSomeItemsToTheEnvelope() + { + $message = new DummyMessage('Hello'); + $envelope = new Envelope($message, array(new ReceivedMessage())); + $envelopeWithAnotherItem = $envelope->with(new AnEnvelopeItem()); + + $firstMiddleware = $this->getMockBuilder(array(MiddlewareInterface::class, EnvelopeAwareInterface::class))->getMock(); + $firstMiddleware->expects($this->once()) + ->method('handle') + ->with($envelope, $this->anything()) + ->will($this->returnCallback(function ($message, $next) { + return $next($message->with(new AnEnvelopeItem())); + })); + + $secondMiddleware = $this->getMockBuilder(MiddlewareInterface::class)->getMock(); + $secondMiddleware->expects($this->once()) + ->method('handle') + ->with($message, $this->anything()) + ->will($this->returnCallback(function ($message, $next) { + return $next($message); + })); + + $thirdMiddleware = $this->getMockBuilder(array(MiddlewareInterface::class, EnvelopeAwareInterface::class))->getMock(); + $thirdMiddleware->expects($this->once()) + ->method('handle') + ->with($envelopeWithAnotherItem, $this->anything()) + ; + + $bus = new MessageBus(array( + $firstMiddleware, + $secondMiddleware, + $thirdMiddleware, + )); + + $bus->dispatch($envelope); + } + + public function testThatAMiddlewareCanUpdateTheMessageWhileKeepingTheEnvelopeItems() + { + $message = new DummyMessage('Hello'); + $envelope = new Envelope($message, $items = array(new ReceivedMessage())); + + $changedMessage = new DummyMessage('Changed'); + $expectedEnvelope = new Envelope($changedMessage, $items); + + $firstMiddleware = $this->getMockBuilder(MiddlewareInterface::class)->getMock(); + $firstMiddleware->expects($this->once()) + ->method('handle') + ->with($message, $this->anything()) + ->will($this->returnCallback(function ($message, $next) use ($changedMessage) { + return $next($changedMessage); + })); + + $secondMiddleware = $this->getMockBuilder(array(MiddlewareInterface::class, EnvelopeAwareInterface::class))->getMock(); + $secondMiddleware->expects($this->once()) + ->method('handle') + ->with($expectedEnvelope, $this->anything()) + ; + + $bus = new MessageBus(array( + $firstMiddleware, + $secondMiddleware, + )); + + $bus->dispatch($envelope); + } +} + +class AnEnvelopeItem implements EnvelopeItemInterface +{ + /** + * {@inheritdoc} + */ + public function serialize() + { + return ''; + } + + /** + * {@inheritdoc} + */ + public function unserialize($serialized) + { + return new self(); + } } From 21e49d21d88133d9998398091695c3cca28d220a Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Mon, 7 May 2018 20:03:43 +0200 Subject: [PATCH 20/76] [Messenger] Fix TraceableBus with envelope --- .../Messenger/Tests/TraceableMessageBusTest.php | 13 +++++++++++++ .../Component/Messenger/TraceableMessageBus.php | 6 ++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Messenger/Tests/TraceableMessageBusTest.php b/src/Symfony/Component/Messenger/Tests/TraceableMessageBusTest.php index 8e4895d3babc8..4da1e5a630202 100644 --- a/src/Symfony/Component/Messenger/Tests/TraceableMessageBusTest.php +++ b/src/Symfony/Component/Messenger/Tests/TraceableMessageBusTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Messenger\Tests; use PHPUnit\Framework\TestCase; +use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; use Symfony\Component\Messenger\TraceableMessageBus; @@ -30,6 +31,18 @@ public function testItTracesResult() $this->assertSame(array(array('message' => $message, 'result' => $result)), $traceableBus->getDispatchedMessages()); } + public function testItTracesResultWithEnvelope() + { + $envelope = Envelope::wrap($message = new DummyMessage('Hello')); + + $bus = $this->getMockBuilder(MessageBusInterface::class)->getMock(); + $bus->expects($this->once())->method('dispatch')->with($envelope)->willReturn($result = array('foo' => 'bar')); + + $traceableBus = new TraceableMessageBus($bus); + $this->assertSame($result, $traceableBus->dispatch($envelope)); + $this->assertSame(array(array('message' => $message, 'result' => $result)), $traceableBus->getDispatchedMessages()); + } + public function testItTracesExceptions() { $message = new DummyMessage('Hello'); diff --git a/src/Symfony/Component/Messenger/TraceableMessageBus.php b/src/Symfony/Component/Messenger/TraceableMessageBus.php index ecf0c5658cce2..9620e0189daca 100644 --- a/src/Symfony/Component/Messenger/TraceableMessageBus.php +++ b/src/Symfony/Component/Messenger/TraceableMessageBus.php @@ -29,18 +29,20 @@ public function __construct(MessageBusInterface $decoratedBus) */ public function dispatch($message) { + $messageToTrace = $message instanceof Envelope ? $message->getMessage() : $message; + try { $result = $this->decoratedBus->dispatch($message); $this->dispatchedMessages[] = array( - 'message' => $message, + 'message' => $messageToTrace, 'result' => $result, ); return $result; } catch (\Throwable $e) { $this->dispatchedMessages[] = array( - 'message' => $message, + 'message' => $messageToTrace, 'exception' => $e, ); From ffa5d1ca9441ef9b45a4b33039990680b1a074f5 Mon Sep 17 00:00:00 2001 From: Xavier HAUSHERR Date: Wed, 9 May 2018 12:00:53 +0200 Subject: [PATCH 21/76] =?UTF-8?q?[Workflow]=C2=A0add=20is=20deprecated=20s?= =?UTF-8?q?ince=20Symfony=204.1.=20Use=20addWorkflow()=20instead?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DependencyInjection/FrameworkExtension.php | 4 ++-- src/Symfony/Bundle/FrameworkBundle/composer.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index ff6351e7078e5..bbdf232fef8cf 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -572,10 +572,10 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ foreach ($workflow['supports'] as $supportedClassName) { $strategyDefinition = new Definition(Workflow\SupportStrategy\InstanceOfSupportStrategy::class, array($supportedClassName)); $strategyDefinition->setPublic(false); - $registryDefinition->addMethodCall('add', array(new Reference($workflowId), $strategyDefinition)); + $registryDefinition->addMethodCall('addWorkflow', array(new Reference($workflowId), $strategyDefinition)); } } elseif (isset($workflow['support_strategy'])) { - $registryDefinition->addMethodCall('add', array(new Reference($workflowId), new Reference($workflow['support_strategy']))); + $registryDefinition->addMethodCall('addWorkflow', array(new Reference($workflowId), new Reference($workflow['support_strategy']))); } // Enable the AuditTrail diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 8c1c32194fdea..08e2f4ead0619 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -51,7 +51,7 @@ "symfony/templating": "~3.4|~4.0", "symfony/validator": "^4.1", "symfony/var-dumper": "~3.4|~4.0", - "symfony/workflow": "~3.4|~4.0", + "symfony/workflow": "^4.1", "symfony/yaml": "~3.4|~4.0", "symfony/property-info": "~3.4|~4.0", "symfony/lock": "~3.4|~4.0", @@ -72,7 +72,7 @@ "symfony/stopwatch": "<3.4", "symfony/translation": "<3.4", "symfony/validator": "<4.1", - "symfony/workflow": "<3.4" + "symfony/workflow": "<4.1" }, "suggest": { "ext-apcu": "For best performance of the system caches", From f2231b584ebbfdbf42db4aab4ca93ce28db67530 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 7 May 2018 15:24:01 -0700 Subject: [PATCH 22/76] [DI] Display previous error messages when throwing unused bindings --- .../Compiler/AbstractRecursivePass.php | 8 ++++++-- .../Compiler/ResolveBindingsPass.php | 12 +++++++++++- .../Tests/Compiler/ResolveBindingsPassTest.php | 16 ++++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php index 5d2d4429e4a97..901dc06ffaee5 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php @@ -118,8 +118,12 @@ protected function getConstructor(Definition $definition, $required) $class = $definition->getClass(); - if (!$r = $this->container->getReflectionClass($class)) { - throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.', $this->currentId, $class)); + try { + if (!$r = $this->container->getReflectionClass($class)) { + throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.', $this->currentId, $class)); + } + } catch (\ReflectionException $e) { + throw new RuntimeException(sprintf('Invalid service "%s": %s.', $this->currentId, lcfirst(rtrim($e->getMessage(), '.')))); } if (!$r = $r->getConstructor()) { if ($required) { diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php index 024523e15585c..9a895cb523258 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php @@ -27,6 +27,7 @@ class ResolveBindingsPass extends AbstractRecursivePass { private $usedBindings = array(); private $unusedBindings = array(); + private $errorMessages = array(); /** * {@inheritdoc} @@ -37,11 +38,19 @@ public function process(ContainerBuilder $container) parent::process($container); foreach ($this->unusedBindings as list($key, $serviceId)) { - throw new InvalidArgumentException(sprintf('Unused binding "%s" in service "%s".', $key, $serviceId)); + $message = sprintf('Unused binding "%s" in service "%s".', $key, $serviceId); + if ($this->errorMessages) { + $message .= sprintf("\nCould be related to%s:", 1 < \count($this->errorMessages) ? ' one of' : ''); + } + foreach ($this->errorMessages as $m) { + $message .= "\n - ".$m; + } + throw new InvalidArgumentException($message); } } finally { $this->usedBindings = array(); $this->unusedBindings = array(); + $this->errorMessages = array(); } } @@ -94,6 +103,7 @@ protected function processValue($value, $isRoot = false) $calls[] = array($constructor, $value->getArguments()); } } catch (RuntimeException $e) { + $this->errorMessages[] = $e->getMessage(); $this->container->getDefinition($this->currentId)->addError($e->getMessage()); return parent::processValue($value, $isRoot); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php index 16e486afafe2e..65f5ceb80fd0c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php @@ -18,6 +18,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy; +use Symfony\Component\DependencyInjection\Tests\Fixtures\ParentNotExists; use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass; use Symfony\Component\DependencyInjection\TypedReference; @@ -61,6 +62,21 @@ public function testUnusedBinding() $pass->process($container); } + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessageRegexp Unused binding "$quz" in service [\s\S]+ Invalid service ".*\\ParentNotExists": class NotExists not found\. + */ + public function testMissingParent() + { + $container = new ContainerBuilder(); + + $definition = $container->register(ParentNotExists::class, ParentNotExists::class); + $definition->setBindings(array('$quz' => '123')); + + $pass = new ResolveBindingsPass(); + $pass->process($container); + } + public function testTypedReferenceSupport() { $container = new ContainerBuilder(); From 301ce5f8391a6b5dc4fd1b365cfa289d8e0a060d Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Mon, 7 May 2018 12:16:37 -0400 Subject: [PATCH 23/76] [Messenger] Make sure Sender and Receiver locators have valid services --- .../Command/ConsumeMessagesCommand.php | 12 +++++------ .../DependencyInjection/MessengerPass.php | 12 +++++++++++ .../DependencyInjection/MessengerPassTest.php | 21 +++++++++++++++++++ 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php b/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php index 72dc2aa907f2d..62892c59f059e 100644 --- a/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php +++ b/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php @@ -14,6 +14,7 @@ use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\RuntimeException; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -22,7 +23,6 @@ use Symfony\Component\Messenger\Transport\Enhancers\StopWhenMemoryUsageIsExceededReceiver; use Symfony\Component\Messenger\Transport\Enhancers\StopWhenMessageCountIsExceededReceiver; use Symfony\Component\Messenger\Transport\Enhancers\StopWhenTimeLimitIsReachedReceiver; -use Symfony\Component\Messenger\Transport\ReceiverInterface; use Symfony\Component\Messenger\Worker; /** @@ -89,12 +89,10 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): void { if (!$this->receiverLocator->has($receiverName = $input->getArgument('receiver'))) { - throw new \RuntimeException(sprintf('Receiver "%s" does not exist.', $receiverName)); + throw new RuntimeException(sprintf('Receiver "%s" does not exist.', $receiverName)); } - if (!($receiver = $this->receiverLocator->get($receiverName)) instanceof ReceiverInterface) { - throw new \RuntimeException(sprintf('Receiver "%s" is not a valid message consumer. It must implement the "%s" interface.', $receiverName, ReceiverInterface::class)); - } + $receiver = $this->receiverLocator->get($receiverName); if ($limit = $input->getOption('limit')) { $receiver = new StopWhenMessageCountIsExceededReceiver($receiver, $limit, $this->logger); @@ -117,9 +115,9 @@ private function convertToBytes(string $memoryLimit): int $memoryLimit = strtolower($memoryLimit); $max = strtolower(ltrim($memoryLimit, '+')); if (0 === strpos($max, '0x')) { - $max = intval($max, 16); + $max = \intval($max, 16); } elseif (0 === strpos($max, '0')) { - $max = intval($max, 8); + $max = \intval($max, 8); } else { $max = (int) $max; } diff --git a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php index 194772814347a..9789ec038cf43 100644 --- a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php +++ b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php @@ -23,6 +23,8 @@ use Symfony\Component\Messenger\Handler\MessageHandlerInterface; use Symfony\Component\Messenger\Handler\MessageSubscriberInterface; use Symfony\Component\Messenger\TraceableMessageBus; +use Symfony\Component\Messenger\Transport\ReceiverInterface; +use Symfony\Component\Messenger\Transport\SenderInterface; /** * @author Samuel Roze @@ -167,6 +169,11 @@ private function registerReceivers(ContainerBuilder $container) $taggedReceivers = $container->findTaggedServiceIds($this->receiverTag); foreach ($taggedReceivers as $id => $tags) { + $receiverClass = $container->findDefinition($id)->getClass(); + if (!is_subclass_of($receiverClass, ReceiverInterface::class)) { + throw new RuntimeException(sprintf('Invalid receiver "%s": class "%s" must implement interface "%s".', $id, $receiverClass, ReceiverInterface::class)); + } + $receiverMapping[$id] = new Reference($id); foreach ($tags as $tag) { @@ -187,6 +194,11 @@ private function registerSenders(ContainerBuilder $container) { $senderLocatorMapping = array(); foreach ($container->findTaggedServiceIds($this->senderTag) as $id => $tags) { + $senderClass = $container->findDefinition($id)->getClass(); + if (!is_subclass_of($senderClass, SenderInterface::class)) { + throw new RuntimeException(sprintf('Invalid sender "%s": class "%s" must implement interface "%s".', $id, $senderClass, SenderInterface::class)); + } + $senderLocatorMapping[$id] = new Reference($id); foreach ($tags as $tag) { diff --git a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php index 37c9aefed4a4d..46983486d7075 100644 --- a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php +++ b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php @@ -172,6 +172,19 @@ public function testItRegistersSenderWithoutTagName() $this->assertEquals(array(AmqpSender::class => new Reference(AmqpSender::class)), $container->getDefinition('messenger.sender_locator')->getArgument(0)); } + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Invalid sender "app.messenger.sender": class "Symfony\Component\Messenger\Tests\DependencyInjection\InvalidSender" must implement interface "Symfony\Component\Messenger\Transport\SenderInterface". + */ + public function testItDoesNotRegisterInvalidSender() + { + $container = $this->getContainerBuilder(); + $container->register('app.messenger.sender', InvalidSender::class) + ->addTag('messenger.sender'); + + (new MessengerPass())->process($container); + } + /** * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException * @expectedExceptionMessage Invalid handler service "Symfony\Component\Messenger\Tests\DependencyInjection\UndefinedMessageHandler": message class "Symfony\Component\Messenger\Tests\DependencyInjection\UndefinedMessage" used as argument type in method "Symfony\Component\Messenger\Tests\DependencyInjection\UndefinedMessageHandler::__invoke()" does not exist. @@ -366,6 +379,14 @@ public function stop(): void } } +class InvalidReceiver +{ +} + +class InvalidSender +{ +} + class UndefinedMessageHandler { public function __invoke(UndefinedMessage $message) From 41e25abf8cdf2cf7e91a1b1828cffc3aeca9c96e Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Mon, 7 May 2018 13:31:27 -0400 Subject: [PATCH 24/76] Fixed return senders based on the message parents/interfaces --- .../Asynchronous/Routing/SenderLocator.php | 22 ++++++-- .../Routing/SenderLocatorTest.php | 52 +++++++++++++++++-- .../Messenger/Tests/Fixtures/DummyMessage.php | 2 +- .../Tests/Fixtures/DummyMessageInterface.php | 7 +++ 4 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 src/Symfony/Component/Messenger/Tests/Fixtures/DummyMessageInterface.php diff --git a/src/Symfony/Component/Messenger/Asynchronous/Routing/SenderLocator.php b/src/Symfony/Component/Messenger/Asynchronous/Routing/SenderLocator.php index 9b62457626c5f..506fe234c6cef 100644 --- a/src/Symfony/Component/Messenger/Asynchronous/Routing/SenderLocator.php +++ b/src/Symfony/Component/Messenger/Asynchronous/Routing/SenderLocator.php @@ -32,13 +32,29 @@ public function __construct(ContainerInterface $senderServiceLocator, array $mes */ public function getSendersForMessage($message): array { - $senderIds = $this->messageToSenderIdsMapping[\get_class($message)] ?? $this->messageToSenderIdsMapping['*'] ?? array(); - $senders = array(); - foreach ($senderIds as $senderId) { + foreach ($this->getSenderIds($message) as $senderId) { $senders[] = $this->senderServiceLocator->get($senderId); } return $senders; } + + private function getSenderIds($message): array + { + if (isset($this->messageToSenderIdsMapping[\get_class($message)])) { + return $this->messageToSenderIdsMapping[\get_class($message)]; + } + if ($messageToSenderIdsMapping = array_intersect_key($this->messageToSenderIdsMapping, class_parents($message))) { + return current($messageToSenderIdsMapping); + } + if ($messageToSenderIdsMapping = array_intersect_key($this->messageToSenderIdsMapping, class_implements($message))) { + return current($messageToSenderIdsMapping); + } + if (isset($this->messageToSenderIdsMapping['*'])) { + return $this->messageToSenderIdsMapping['*']; + } + + return array(); + } } diff --git a/src/Symfony/Component/Messenger/Tests/Asynchronous/Routing/SenderLocatorTest.php b/src/Symfony/Component/Messenger/Tests/Asynchronous/Routing/SenderLocatorTest.php index caf952526439a..6278be85c57dc 100644 --- a/src/Symfony/Component/Messenger/Tests/Asynchronous/Routing/SenderLocatorTest.php +++ b/src/Symfony/Component/Messenger/Tests/Asynchronous/Routing/SenderLocatorTest.php @@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\Container; use Symfony\Component\Messenger\Asynchronous\Routing\SenderLocator; use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; +use Symfony\Component\Messenger\Tests\Fixtures\DummyMessageInterface; use Symfony\Component\Messenger\Tests\Fixtures\SecondMessage; use Symfony\Component\Messenger\Transport\SenderInterface; @@ -32,8 +33,47 @@ public function testItReturnsTheSenderBasedOnTheMessageClass() ), )); - $this->assertEquals(array($sender), $locator->getSendersForMessage(new DummyMessage('Hello'))); - $this->assertEquals(array(), $locator->getSendersForMessage(new SecondMessage())); + $this->assertSame(array($sender), $locator->getSendersForMessage(new DummyMessage('Hello'))); + $this->assertSame(array(), $locator->getSendersForMessage(new SecondMessage())); + } + + public function testItReturnsTheSenderBasedOnTheMessageParentClass() + { + $container = new Container(); + + $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); + $container->set('my_amqp_sender', $sender); + + $apiSender = $this->getMockBuilder(SenderInterface::class)->getMock(); + $container->set('my_api_sender', $apiSender); + + $locator = new SenderLocator($container, array( + DummyMessageInterface::class => array( + 'my_api_sender', + ), + DummyMessage::class => array( + 'my_amqp_sender', + ), + )); + $this->assertSame(array($sender), $locator->getSendersForMessage(new ChildDummyMessage('Hello'))); + $this->assertSame(array(), $locator->getSendersForMessage(new SecondMessage())); + } + + public function testItReturnsTheSenderBasedOnTheMessageInterface() + { + $container = new Container(); + + $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); + $container->set('my_amqp_sender', $sender); + + $locator = new SenderLocator($container, array( + DummyMessageInterface::class => array( + 'my_amqp_sender', + ), + )); + + $this->assertSame(array($sender), $locator->getSendersForMessage(new DummyMessage('Hello'))); + $this->assertSame(array(), $locator->getSendersForMessage(new SecondMessage())); } public function testItSupportsAWildcardInsteadOfTheMessageClass() @@ -55,7 +95,11 @@ public function testItSupportsAWildcardInsteadOfTheMessageClass() ), )); - $this->assertEquals(array($sender), $locator->getSendersForMessage(new DummyMessage('Hello'))); - $this->assertEquals(array($apiSender), $locator->getSendersForMessage(new SecondMessage())); + $this->assertSame(array($sender), $locator->getSendersForMessage(new DummyMessage('Hello'))); + $this->assertSame(array($apiSender), $locator->getSendersForMessage(new SecondMessage())); } } + +class ChildDummyMessage extends DummyMessage +{ +} diff --git a/src/Symfony/Component/Messenger/Tests/Fixtures/DummyMessage.php b/src/Symfony/Component/Messenger/Tests/Fixtures/DummyMessage.php index fb02ca7b866ae..2a9c70b1c5349 100644 --- a/src/Symfony/Component/Messenger/Tests/Fixtures/DummyMessage.php +++ b/src/Symfony/Component/Messenger/Tests/Fixtures/DummyMessage.php @@ -2,7 +2,7 @@ namespace Symfony\Component\Messenger\Tests\Fixtures; -class DummyMessage +class DummyMessage implements DummyMessageInterface { private $message; diff --git a/src/Symfony/Component/Messenger/Tests/Fixtures/DummyMessageInterface.php b/src/Symfony/Component/Messenger/Tests/Fixtures/DummyMessageInterface.php new file mode 100644 index 0000000000000..557b958ddc62e --- /dev/null +++ b/src/Symfony/Component/Messenger/Tests/Fixtures/DummyMessageInterface.php @@ -0,0 +1,7 @@ + Date: Thu, 10 May 2018 14:27:02 +0200 Subject: [PATCH 25/76] [Messenger] Fix new AMQP Transport test with Envelope --- .../Tests/Transport/AmqpExt/AmqpTransportTest.php | 7 ++++--- .../Messenger/Transport/AmqpExt/AmqpTransport.php | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpTransportTest.php b/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpTransportTest.php index 26f3261577cae..3d003a270d062 100644 --- a/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpTransportTest.php +++ b/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/AmqpTransportTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Messenger\Tests\Transport\AmqpExt; use PHPUnit\Framework\TestCase; +use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; use Symfony\Component\Messenger\Transport\AmqpExt\AmqpTransport; use Symfony\Component\Messenger\Transport\AmqpExt\Connection; @@ -45,11 +46,11 @@ public function testReceivesMessages() $amqpEnvelope->method('getBody')->willReturn('body'); $amqpEnvelope->method('getHeaders')->willReturn(array('my' => 'header')); - $decoder->method('decode')->with(array('body' => 'body', 'headers' => array('my' => 'header')))->willReturn($decodedMessage); + $decoder->method('decode')->with(array('body' => 'body', 'headers' => array('my' => 'header')))->willReturn(Envelope::wrap($decodedMessage)); $connection->method('get')->willReturn($amqpEnvelope); - $transport->receive(function ($message) use ($transport, $decodedMessage) { - $this->assertSame($decodedMessage, $message); + $transport->receive(function (Envelope $envelope) use ($transport, $decodedMessage) { + $this->assertSame($decodedMessage, $envelope->getMessage()); $transport->stop(); }); diff --git a/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransport.php b/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransport.php index 583ec03baefbb..3edefd0ab1c8a 100644 --- a/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransport.php +++ b/src/Symfony/Component/Messenger/Transport/AmqpExt/AmqpTransport.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Messenger\Transport\AmqpExt; +use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Transport\Serialization\DecoderInterface; use Symfony\Component\Messenger\Transport\Serialization\EncoderInterface; use Symfony\Component\Messenger\Transport\TransportInterface; @@ -52,9 +53,9 @@ public function stop(): void /** * {@inheritdoc} */ - public function send($message): void + public function send(Envelope $envelope): void { - ($this->sender ?? $this->getSender())->send($message); + ($this->sender ?? $this->getSender())->send($envelope); } private function getReceiver() From 3d1ab6df13f04f29458b9bb8f1a5c77e6a98bab9 Mon Sep 17 00:00:00 2001 From: fsevestre Date: Thu, 10 May 2018 16:33:41 +0200 Subject: [PATCH 26/76] Fix misses calculation when calling getItems --- .../Component/Cache/DataCollector/CacheDataCollector.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php b/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php index ceef45aa0b185..962fb25cb94cb 100644 --- a/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php +++ b/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php @@ -132,10 +132,9 @@ private function calculateStatistics() $statistics[$name]['misses'] += 1; } } elseif ('getItems' === $call->name) { - $count = $call->hits + $call->misses; - $statistics[$name]['reads'] += $count; + $statistics[$name]['reads'] += $call->hits + $call->misses; $statistics[$name]['hits'] += $call->hits; - $statistics[$name]['misses'] += $count - $call->misses; + $statistics[$name]['misses'] += $call->misses; } elseif ('hasItem' === $call->name) { $statistics[$name]['reads'] += 1; if (false === $call->result) { From 6295879a3080482bf996beca9ea080a2e6c2274a Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Thu, 10 May 2018 13:03:14 -0400 Subject: [PATCH 27/76] Autoconfiguring TransportFactoryInterface classes --- .../FrameworkBundle/DependencyInjection/FrameworkExtension.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index c08ad7b3903af..9f2075057a14d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -65,6 +65,7 @@ use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Messenger\Transport\ReceiverInterface; use Symfony\Component\Messenger\Transport\SenderInterface; +use Symfony\Component\Messenger\Transport\TransportFactoryInterface; use Symfony\Component\Messenger\Transport\TransportInterface; use Symfony\Component\PropertyAccess\PropertyAccessor; use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface; @@ -349,6 +350,8 @@ public function load(array $configs, ContainerBuilder $container) ->addTag('messenger.sender'); $container->registerForAutoconfiguration(MessageHandlerInterface::class) ->addTag('messenger.message_handler'); + $container->registerForAutoconfiguration(TransportFactoryInterface::class) + ->addTag('messenger.transport_factory'); if (!$container->getParameter('kernel.debug')) { // remove tagged iterator argument for resource checkers From db6ef38ea9a5ff1c7198159984a9a714de55c917 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 10 May 2018 11:44:29 -0700 Subject: [PATCH 28/76] [HttpKernel] Handle NoConfigurationException "onKernelException()" --- .../EventListener/RouterListener.php | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php b/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php index caaf80f86b892..959f2ba0d1135 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php @@ -14,6 +14,7 @@ use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; use Symfony\Component\HttpKernel\Event\FinishRequestEvent; use Symfony\Component\HttpKernel\Kernel; use Symfony\Component\HttpKernel\KernelEvents; @@ -129,12 +130,6 @@ public function onKernelRequest(GetResponseEvent $event) unset($parameters['_route'], $parameters['_controller']); $request->attributes->set('_route_params', $parameters); } catch (ResourceNotFoundException $e) { - if ($this->debug && $e instanceof NoConfigurationException) { - $event->setResponse($this->createWelcomeResponse()); - - return; - } - $message = sprintf('No route found for "%s %s"', $request->getMethod(), $request->getPathInfo()); if ($referer = $request->headers->get('referer')) { @@ -149,11 +144,23 @@ public function onKernelRequest(GetResponseEvent $event) } } + public function onKernelException(GetResponseForExceptionEvent $event) + { + if (!$this->debug || !($e = $event->getException()) instanceof NotFoundHttpException) { + return; + } + + if ($e->getPrevious() instanceof NoConfigurationException) { + $event->setResponse($this->createWelcomeResponse()); + } + } + public static function getSubscribedEvents() { return array( KernelEvents::REQUEST => array(array('onKernelRequest', 32)), KernelEvents::FINISH_REQUEST => array(array('onKernelFinishRequest', 0)), + KernelEvents::EXCEPTION => array('onKernelException', -64), ); } From 1ef27a7e6a10a24a023a4f136d60d6ebda35e5eb Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Thu, 10 May 2018 17:13:07 -0400 Subject: [PATCH 29/76] Rename tag attribute "name" by "alias" --- .../DependencyInjection/FrameworkExtension.php | 4 ++-- .../DependencyInjection/FrameworkExtensionTest.php | 4 ++-- .../Messenger/DependencyInjection/MessengerPass.php | 12 ++++++------ .../Tests/DependencyInjection/MessengerPassTest.php | 10 +++++----- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index c08ad7b3903af..af449c1a54988 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -1511,8 +1511,8 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder $transportDefinition = (new Definition(TransportInterface::class)) ->setFactory(array(new Reference('messenger.transport_factory'), 'createTransport')) ->setArguments(array($transport['dsn'], $transport['options'])) - ->addTag('messenger.receiver', array('name' => $name)) - ->addTag('messenger.sender', array('name' => $name)) + ->addTag('messenger.receiver', array('alias' => $name)) + ->addTag('messenger.sender', array('alias' => $name)) ; $container->setDefinition('messenger.transport.'.$name, $transportDefinition); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index 8d94d32aa83b0..cbed3c89f8596 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -539,8 +539,8 @@ public function testMessengerTransports() $this->assertTrue($container->hasDefinition('messenger.transport.default')); $this->assertTrue($container->getDefinition('messenger.transport.default')->hasTag('messenger.receiver')); $this->assertTrue($container->getDefinition('messenger.transport.default')->hasTag('messenger.sender')); - $this->assertEquals(array(array('name' => 'default')), $container->getDefinition('messenger.transport.default')->getTag('messenger.receiver')); - $this->assertEquals(array(array('name' => 'default')), $container->getDefinition('messenger.transport.default')->getTag('messenger.sender')); + $this->assertEquals(array(array('alias' => 'default')), $container->getDefinition('messenger.transport.default')->getTag('messenger.receiver')); + $this->assertEquals(array(array('alias' => 'default')), $container->getDefinition('messenger.transport.default')->getTag('messenger.sender')); $this->assertTrue($container->hasDefinition('messenger.transport.customised')); $transportFactory = $container->getDefinition('messenger.transport.customised')->getFactory(); diff --git a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php index 9789ec038cf43..81f84617064cc 100644 --- a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php +++ b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php @@ -63,7 +63,7 @@ public function process(ContainerBuilder $container) } if ($container->hasDefinition('messenger.data_collector')) { - $this->registerBusToCollector($container, $busId, $tags[0]); + $this->registerBusToCollector($container, $busId); } } @@ -177,8 +177,8 @@ private function registerReceivers(ContainerBuilder $container) $receiverMapping[$id] = new Reference($id); foreach ($tags as $tag) { - if (isset($tag['name'])) { - $receiverMapping[$tag['name']] = $receiverMapping[$id]; + if (isset($tag['alias'])) { + $receiverMapping[$tag['alias']] = $receiverMapping[$id]; } } } @@ -202,8 +202,8 @@ private function registerSenders(ContainerBuilder $container) $senderLocatorMapping[$id] = new Reference($id); foreach ($tags as $tag) { - if (isset($tag['name'])) { - $senderLocatorMapping[$tag['name']] = $senderLocatorMapping[$id]; + if (isset($tag['alias'])) { + $senderLocatorMapping[$tag['alias']] = $senderLocatorMapping[$id]; } } } @@ -211,7 +211,7 @@ private function registerSenders(ContainerBuilder $container) $container->getDefinition('messenger.sender_locator')->replaceArgument(0, $senderLocatorMapping); } - private function registerBusToCollector(ContainerBuilder $container, string $busId, array $tag) + private function registerBusToCollector(ContainerBuilder $container, string $busId) { $container->setDefinition( $tracedBusId = 'debug.traced.'.$busId, diff --git a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php index eeb5d585f5cda..d49a38421b1d3 100644 --- a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php +++ b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php @@ -101,7 +101,7 @@ public function testGetClassesFromTheHandlerSubscriberInterface() public function testItRegistersReceivers() { $container = $this->getContainerBuilder(); - $container->register(AmqpReceiver::class, AmqpReceiver::class)->addTag('messenger.receiver', array('name' => 'amqp')); + $container->register(AmqpReceiver::class, AmqpReceiver::class)->addTag('messenger.receiver', array('alias' => 'amqp')); (new MessengerPass())->process($container); @@ -128,7 +128,7 @@ public function testItRegistersOneReceiverAndSetsTheDefaultOneOnTheCommand() null, )); - $container->register(AmqpReceiver::class, AmqpReceiver::class)->addTag('messenger.receiver', array('name' => 'amqp')); + $container->register(AmqpReceiver::class, AmqpReceiver::class)->addTag('messenger.receiver', array('alias' => 'amqp')); (new MessengerPass())->process($container); @@ -145,8 +145,8 @@ public function testItRegistersMultipleReceiversAndDoesNotSetTheDefaultOneOnTheC null, )); - $container->register(AmqpReceiver::class, AmqpReceiver::class)->addTag('messenger.receiver', array('name' => 'amqp')); - $container->register(DummyReceiver::class, DummyReceiver::class)->addTag('messenger.receiver', array('name' => 'dummy')); + $container->register(AmqpReceiver::class, AmqpReceiver::class)->addTag('messenger.receiver', array('alias' => 'amqp')); + $container->register(DummyReceiver::class, DummyReceiver::class)->addTag('messenger.receiver', array('alias' => 'dummy')); (new MessengerPass())->process($container); @@ -156,7 +156,7 @@ public function testItRegistersMultipleReceiversAndDoesNotSetTheDefaultOneOnTheC public function testItRegistersSenders() { $container = $this->getContainerBuilder(); - $container->register(AmqpSender::class, AmqpSender::class)->addTag('messenger.sender', array('name' => 'amqp')); + $container->register(AmqpSender::class, AmqpSender::class)->addTag('messenger.sender', array('alias' => 'amqp')); (new MessengerPass())->process($container); From ee44903fd060fdf714d0d17144541a9d9bed115d Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Wed, 9 May 2018 21:15:27 +0200 Subject: [PATCH 30/76] [HttpKernel] Fix services are no longer injected into __invoke controllers method --- .../RegisterControllerArgumentLocatorsPass.php | 8 ++++++-- .../RemoveEmptyControllerArgumentLocatorsPass.php | 13 ++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php index cd76e56caab83..490b595b94153 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php @@ -33,11 +33,13 @@ class RegisterControllerArgumentLocatorsPass implements CompilerPassInterface { private $resolverServiceId; private $controllerTag; + private $controllerLocator; - public function __construct(string $resolverServiceId = 'argument_resolver.service', string $controllerTag = 'controller.service_arguments') + public function __construct(string $resolverServiceId = 'argument_resolver.service', string $controllerTag = 'controller.service_arguments', string $controllerLocator = 'argument_resolver.controller_locator') { $this->resolverServiceId = $resolverServiceId; $this->controllerTag = $controllerTag; + $this->controllerLocator = $controllerLocator; } public function process(ContainerBuilder $container) @@ -179,6 +181,8 @@ public function process(ContainerBuilder $container) } $container->getDefinition($this->resolverServiceId) - ->replaceArgument(0, ServiceLocatorTagPass::register($container, $controllers)); + ->replaceArgument(0, $controllerLocatorRef = ServiceLocatorTagPass::register($container, $controllers)); + + $container->setAlias($this->controllerLocator, (string) $controllerLocatorRef); } } diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php index b7d64994cec87..596b6188f66cb 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php @@ -21,21 +21,16 @@ */ class RemoveEmptyControllerArgumentLocatorsPass implements CompilerPassInterface { - private $resolverServiceId; + private $controllerLocator; - public function __construct(string $resolverServiceId = 'argument_resolver.service') + public function __construct(string $controllerLocator = 'argument_resolver.controller_locator') { - $this->resolverServiceId = $resolverServiceId; + $this->controllerLocator = $controllerLocator; } public function process(ContainerBuilder $container) { - if (false === $container->hasDefinition($this->resolverServiceId)) { - return; - } - - $serviceResolver = $container->getDefinition($this->resolverServiceId); - $controllerLocator = $container->getDefinition((string) $serviceResolver->getArgument(0)); + $controllerLocator = $container->findDefinition($this->controllerLocator); $controllers = $controllerLocator->getArgument(0); foreach ($controllers as $controller => $argumentRef) { From 63871c9ce49086e5c97075af957ccb63600f2809 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Thu, 10 May 2018 12:41:21 -0400 Subject: [PATCH 31/76] [Messenger] Make sure default receiver name is set before command configuration --- .../Command/ConsumeMessagesCommand.php | 4 +-- .../Command/ConsumeMessagesCommandTest.php | 36 +++++++++++++++++++ src/Symfony/Component/Messenger/composer.json | 1 + 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php diff --git a/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php b/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php index 62892c59f059e..e64d10f1953e7 100644 --- a/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php +++ b/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php @@ -41,12 +41,12 @@ class ConsumeMessagesCommand extends Command public function __construct(MessageBusInterface $bus, ContainerInterface $receiverLocator, LoggerInterface $logger = null, string $defaultReceiverName = null) { - parent::__construct(); - $this->bus = $bus; $this->receiverLocator = $receiverLocator; $this->logger = $logger; $this->defaultReceiverName = $defaultReceiverName; + + parent::__construct(); } /** diff --git a/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php new file mode 100644 index 0000000000000..4eca2423aa47c --- /dev/null +++ b/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Tests\Command; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ServiceLocator; +use Symfony\Component\Messenger\Command\ConsumeMessagesCommand; +use Symfony\Component\Messenger\MessageBus; + +class ConsumeMessagesCommandTest extends TestCase +{ + public function testConfigurationWithDefaultReceiver() + { + $command = new ConsumeMessagesCommand($this->createMock(MessageBus::class), $this->createMock(ServiceLocator::class), null, 'messenger.transport.amqp'); + $inputArgument = $command->getDefinition()->getArgument('receiver'); + $this->assertFalse($inputArgument->isRequired()); + $this->assertSame('messenger.transport.amqp', $inputArgument->getDefault()); + } + + public function testConfigurationWithoutDefaultReceiver() + { + $command = new ConsumeMessagesCommand($this->createMock(MessageBus::class), $this->createMock(ServiceLocator::class)); + $inputArgument = $command->getDefinition()->getArgument('receiver'); + $this->assertTrue($inputArgument->isRequired()); + $this->assertNull($inputArgument->getDefault()); + } +} diff --git a/src/Symfony/Component/Messenger/composer.json b/src/Symfony/Component/Messenger/composer.json index 1767f4dab967e..adbd0e2a9f671 100644 --- a/src/Symfony/Component/Messenger/composer.json +++ b/src/Symfony/Component/Messenger/composer.json @@ -20,6 +20,7 @@ }, "require-dev": { "psr/log": "~1.0", + "symfony/console": "~3.4|~4.0", "symfony/dependency-injection": "~3.4.6|~4.0", "symfony/http-kernel": "~3.4|~4.0", "symfony/process": "~3.4|~4.0", From fa4ce7bbc44fc5a16e9402d7e33845f0f13d963d Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Thu, 10 May 2018 15:56:57 -0400 Subject: [PATCH 32/76] [Messenger] remove autoconfiguration for Sender/ReceiverInterface --- .../DependencyInjection/FrameworkExtension.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index fa853e0940900..bc41753a7b342 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -63,8 +63,6 @@ use Symfony\Component\Messenger\Handler\MessageHandlerInterface; use Symfony\Component\Messenger\MessageBus; use Symfony\Component\Messenger\MessageBusInterface; -use Symfony\Component\Messenger\Transport\ReceiverInterface; -use Symfony\Component\Messenger\Transport\SenderInterface; use Symfony\Component\Messenger\Transport\TransportFactoryInterface; use Symfony\Component\Messenger\Transport\TransportInterface; use Symfony\Component\PropertyAccess\PropertyAccessor; @@ -344,10 +342,6 @@ public function load(array $configs, ContainerBuilder $container) ->addTag('validator.constraint_validator'); $container->registerForAutoconfiguration(ObjectInitializerInterface::class) ->addTag('validator.initializer'); - $container->registerForAutoconfiguration(ReceiverInterface::class) - ->addTag('messenger.receiver'); - $container->registerForAutoconfiguration(SenderInterface::class) - ->addTag('messenger.sender'); $container->registerForAutoconfiguration(MessageHandlerInterface::class) ->addTag('messenger.message_handler'); $container->registerForAutoconfiguration(TransportFactoryInterface::class) From 2461e5119ae8de182b88eb8cbb49fb4df0864b34 Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Tue, 24 Apr 2018 17:35:44 +0100 Subject: [PATCH 33/76] [Messenger][DX] Uses custom method names for handlers --- .../DependencyInjection/MessengerPass.php | 37 ++++++- .../Handler/MessageSubscriberInterface.php | 11 ++- .../DependencyInjection/MessengerPassTest.php | 96 ++++++++++++++++++- 3 files changed, 132 insertions(+), 12 deletions(-) diff --git a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php index 9789ec038cf43..230676d8eef88 100644 --- a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php +++ b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php @@ -74,14 +74,27 @@ public function process(ContainerBuilder $container) private function registerHandlers(ContainerBuilder $container) { + $definitions = array(); $handlersByMessage = array(); foreach ($container->findTaggedServiceIds($this->handlerTag, true) as $serviceId => $tags) { foreach ($tags as $tag) { - $handles = isset($tag['handles']) ? array($tag['handles']) : $this->guessHandledClasses($r = $container->getReflectionClass($container->getDefinition($serviceId)->getClass()), $serviceId); + $r = $container->getReflectionClass($container->getDefinition($serviceId)->getClass()); + + if (isset($tag['handles'])) { + $handles = isset($tag['method']) ? array($tag['handles'] => $tag['method']) : array($tag['handles']); + } else { + $handles = $this->guessHandledClasses($r, $serviceId); + } + $priority = $tag['priority'] ?? 0; - foreach ($handles as $messageClass) { + foreach ($handles as $messageClass => $method) { + if (\is_int($messageClass)) { + $messageClass = $method; + $method = '__invoke'; + } + if (\is_array($messageClass)) { $messagePriority = $messageClass[1]; $messageClass = $messageClass[0]; @@ -89,12 +102,27 @@ private function registerHandlers(ContainerBuilder $container) $messagePriority = $priority; } - if (!class_exists($messageClass)) { - $messageClassLocation = isset($tag['handles']) ? 'declared in your tag attribute "handles"' : sprintf($r->implementsInterface(MessageHandlerInterface::class) ? 'returned by method "%s::getHandledMessages()"' : 'used as argument type in method "%s::__invoke()"', $r->getName()); + if (\is_array($method)) { + $messagePriority = $method[1]; + $method = $method[0]; + } + + if (!\class_exists($messageClass)) { + $messageClassLocation = isset($tag['handles']) ? 'declared in your tag attribute "handles"' : $r->implementsInterface(MessageHandlerInterface::class) ? sprintf('returned by method "%s::getHandledMessages()"', $r->getName()) : sprintf('used as argument type in method "%s::%s()"', $r->getName(), $method); throw new RuntimeException(sprintf('Invalid handler service "%s": message class "%s" %s does not exist.', $serviceId, $messageClass, $messageClassLocation)); } + if (!$r->hasMethod($method)) { + throw new RuntimeException(sprintf('Invalid handler service "%s": method "%s::%s()" does not exist.', $serviceId, $r->getName(), $method)); + } + + if ('__invoke' !== $method) { + $wrapperDefinition = (new Definition('callable'))->addArgument(array(new Reference($serviceId), $method))->setFactory('Closure::fromCallable'); + + $definitions[$serviceId = '.messenger.method_on_object_wrapper.'.ContainerBuilder::hash($messageClass.':'.$messagePriority.':'.$serviceId.':'.$method)] = $wrapperDefinition; + } + $handlersByMessage[$messageClass][$messagePriority][] = new Reference($serviceId); } } @@ -105,7 +133,6 @@ private function registerHandlers(ContainerBuilder $container) $handlersByMessage[$message] = array_merge(...$handlersByMessage[$message]); } - $definitions = array(); $handlersLocatorMapping = array(); foreach ($handlersByMessage as $message => $handlers) { if (1 === \count($handlers)) { diff --git a/src/Symfony/Component/Messenger/Handler/MessageSubscriberInterface.php b/src/Symfony/Component/Messenger/Handler/MessageSubscriberInterface.php index 6a751482bff0b..ba4ae3ee79870 100644 --- a/src/Symfony/Component/Messenger/Handler/MessageSubscriberInterface.php +++ b/src/Symfony/Component/Messenger/Handler/MessageSubscriberInterface.php @@ -34,9 +34,14 @@ interface MessageSubscriberInterface extends MessageHandlerInterface * [SecondMessage::class, -10], * ]; * - * The `__invoke` method of the handler will be called as usual with the message to handle. + * It can also specify a method and/or a priority per message: * - * @return array + * return [ + * FirstMessage::class => 'firstMessageMethod', + * SecondMessage::class => ['secondMessageMethod', 20], + * ]; + * + * The `__invoke` method of the handler will be called as usual with the message to handle. */ - public static function getHandledMessages(): array; + public static function getHandledMessages(): iterable; } diff --git a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php index eeb5d585f5cda..ac32461da6a27 100644 --- a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php +++ b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php @@ -98,6 +98,53 @@ public function testGetClassesFromTheHandlerSubscriberInterface() $this->assertEquals(array(new Reference(PrioritizedHandler::class), new Reference(HandlerWithMultipleMessages::class)), $definition->getArgument(0)); } + public function testGetClassesAndMethodsAndPrioritiesFromTheSubscriber() + { + $container = $this->getContainerBuilder(); + $container + ->register(HandlerMappingMethods::class, HandlerMappingMethods::class) + ->addTag('messenger.message_handler') + ; + $container + ->register(PrioritizedHandler::class, PrioritizedHandler::class) + ->addTag('messenger.message_handler') + ; + + (new MessengerPass())->process($container); + + $handlerLocatorDefinition = $container->getDefinition($container->getDefinition('messenger.handler_resolver')->getArgument(0)); + $handlerMapping = $handlerLocatorDefinition->getArgument(0); + + $this->assertArrayHasKey('handler.'.DummyMessage::class, $handlerMapping); + $this->assertArrayHasKey('handler.'.SecondMessage::class, $handlerMapping); + + $dummyHandlerReference = (string) $handlerMapping['handler.'.DummyMessage::class]->getValues()[0]; + $dummyHandlerDefinition = $container->getDefinition($dummyHandlerReference); + $this->assertSame('callable', $dummyHandlerDefinition->getClass()); + $this->assertEquals(array(new Reference(HandlerMappingMethods::class), 'dummyMethod'), $dummyHandlerDefinition->getArgument(0)); + $this->assertSame(array('Closure', 'fromCallable'), $dummyHandlerDefinition->getFactory()); + + $secondHandlerReference = (string) $handlerMapping['handler.'.SecondMessage::class]->getValues()[0]; + $secondHandlerDefinition = $container->getDefinition($secondHandlerReference); + $this->assertSame(ChainHandler::class, $secondHandlerDefinition->getClass()); + $this->assertEquals(new Reference(PrioritizedHandler::class), $secondHandlerDefinition->getArgument(0)[1]); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Invalid handler service "Symfony\Component\Messenger\Tests\DependencyInjection\HandlerMappingWithNonExistentMethod": method "Symfony\Component\Messenger\Tests\DependencyInjection\HandlerMappingWithNonExistentMethod::dummyMethod()" does not exist. + */ + public function testThrowsExceptionIfTheHandlerMethodDoesNotExist() + { + $container = $this->getContainerBuilder(); + $container + ->register(HandlerMappingWithNonExistentMethod::class, HandlerMappingWithNonExistentMethod::class) + ->addTag('messenger.message_handler') + ; + + (new MessengerPass())->process($container); + } + public function testItRegistersReceivers() { $container = $this->getContainerBuilder(); @@ -397,7 +444,7 @@ public function __invoke(UndefinedMessage $message) class UndefinedMessageHandlerViaInterface implements MessageSubscriberInterface { - public static function getHandledMessages(): array + public static function getHandledMessages(): iterable { return array(UndefinedMessage::class); } @@ -434,31 +481,72 @@ public function __invoke(string $message) class HandlerWithMultipleMessages implements MessageSubscriberInterface { - public static function getHandledMessages(): array + public static function getHandledMessages(): iterable { return array( DummyMessage::class, SecondMessage::class, ); } + + public function __invoke() + { + } } class PrioritizedHandler implements MessageSubscriberInterface { - public static function getHandledMessages(): array + public static function getHandledMessages(): iterable { return array( array(SecondMessage::class, 10), ); } + + public function __invoke() + { + } +} + +class HandlerMappingMethods implements MessageSubscriberInterface +{ + public static function getHandledMessages(): iterable + { + return array( + DummyMessage::class => 'dummyMethod', + SecondMessage::class => array('secondMessage', 20), + ); + } + + public function dummyMethod() + { + } + + public function secondMessage() + { + } +} + +class HandlerMappingWithNonExistentMethod implements MessageSubscriberInterface +{ + public static function getHandledMessages(): iterable + { + return array( + DummyMessage::class => 'dummyMethod', + ); + } } class HandleNoMessageHandler implements MessageSubscriberInterface { - public static function getHandledMessages(): array + public static function getHandledMessages(): iterable { return array(); } + + public function __invoke() + { + } } class UselessMiddleware implements MiddlewareInterface From 54c2541d94b38e4af9ca167e5864515cf3d16614 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Thu, 10 May 2018 16:58:59 -0400 Subject: [PATCH 34/76] Select alternatives on missing receiver arg or typo --- .../Command/ConsumeMessagesCommand.php | 49 +++++++++++++++++-- .../DependencyInjection/MessengerPass.php | 11 +++-- .../Command/ConsumeMessagesCommandTest.php | 6 +-- .../DependencyInjection/MessengerPassTest.php | 21 +------- 4 files changed, 57 insertions(+), 30 deletions(-) diff --git a/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php b/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php index e64d10f1953e7..06b48a50c5ac3 100644 --- a/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php +++ b/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php @@ -19,6 +19,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Messenger\Transport\Enhancers\StopWhenMemoryUsageIsExceededReceiver; use Symfony\Component\Messenger\Transport\Enhancers\StopWhenMessageCountIsExceededReceiver; @@ -37,14 +38,14 @@ class ConsumeMessagesCommand extends Command private $bus; private $receiverLocator; private $logger; - private $defaultReceiverName; + private $receiverNames; - public function __construct(MessageBusInterface $bus, ContainerInterface $receiverLocator, LoggerInterface $logger = null, string $defaultReceiverName = null) + public function __construct(MessageBusInterface $bus, ContainerInterface $receiverLocator, LoggerInterface $logger = null, array $receiverNames = array()) { $this->bus = $bus; $this->receiverLocator = $receiverLocator; $this->logger = $logger; - $this->defaultReceiverName = $defaultReceiverName; + $this->receiverNames = $receiverNames; parent::__construct(); } @@ -54,9 +55,11 @@ public function __construct(MessageBusInterface $bus, ContainerInterface $receiv */ protected function configure(): void { + $defaultReceiverName = 1 === \count($this->receiverNames) ? current($this->receiverNames) : null; + $this ->setDefinition(array( - new InputArgument('receiver', $this->defaultReceiverName ? InputArgument::OPTIONAL : InputArgument::REQUIRED, 'Name of the receiver', $this->defaultReceiverName), + new InputArgument('receiver', $defaultReceiverName ? InputArgument::OPTIONAL : InputArgument::REQUIRED, 'Name of the receiver', $defaultReceiverName), new InputOption('limit', 'l', InputOption::VALUE_REQUIRED, 'Limit the number of received messages'), new InputOption('memory-limit', 'm', InputOption::VALUE_REQUIRED, 'The memory limit the worker can consume'), new InputOption('time-limit', 't', InputOption::VALUE_REQUIRED, 'The time limit in seconds the worker can run'), @@ -83,6 +86,27 @@ protected function configure(): void ; } + /** + * {@inheritdoc} + */ + protected function interact(InputInterface $input, OutputInterface $output) + { + if (!$this->receiverNames || $this->receiverLocator->has($receiverName = $input->getArgument('receiver'))) { + return; + } + + $style = new SymfonyStyle($input, $output); + if (null === $receiverName) { + $style->block('Missing receiver argument.', null, 'error', ' ', true); + $input->setArgument('receiver', $style->choice('Select one of the available receivers', $this->receiverNames)); + } elseif ($alternatives = $this->findAlternatives($receiverName, $this->receiverNames)) { + $style->block(sprintf('Receiver "%s" is not defined.', $receiverName), null, 'error', ' ', true); + if ($style->confirm(sprintf('Do you want to receive from "%s" instead? ', $alternatives[0]), false)) { + $input->setArgument('receiver', $alternatives[0]); + } + } + } + /** * {@inheritdoc} */ @@ -134,4 +158,21 @@ private function convertToBytes(string $memoryLimit): int return $max; } + + private function findAlternatives($name, array $collection) + { + $alternatives = array(); + foreach ($collection as $item) { + $lev = levenshtein($name, $item); + if ($lev <= \strlen($name) / 3 || false !== strpos($item, $name)) { + $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev; + } + } + + $threshold = 1e3; + $alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; }); + ksort($alternatives, SORT_NATURAL | SORT_FLAG_CASE); + + return array_keys($alternatives); + } } diff --git a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php index 81f84617064cc..88e468770c001 100644 --- a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php +++ b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php @@ -166,9 +166,8 @@ private function guessHandledClasses(\ReflectionClass $handlerClass, string $ser private function registerReceivers(ContainerBuilder $container) { $receiverMapping = array(); - $taggedReceivers = $container->findTaggedServiceIds($this->receiverTag); - foreach ($taggedReceivers as $id => $tags) { + foreach ($container->findTaggedServiceIds($this->receiverTag) as $id => $tags) { $receiverClass = $container->findDefinition($id)->getClass(); if (!is_subclass_of($receiverClass, ReceiverInterface::class)) { throw new RuntimeException(sprintf('Invalid receiver "%s": class "%s" must implement interface "%s".', $id, $receiverClass, ReceiverInterface::class)); @@ -183,8 +182,12 @@ private function registerReceivers(ContainerBuilder $container) } } - if (1 === \count($taggedReceivers) && $container->hasDefinition('console.command.messenger_consume_messages')) { - $container->getDefinition('console.command.messenger_consume_messages')->replaceArgument(3, (string) current($receiverMapping)); + if ($container->hasDefinition('console.command.messenger_consume_messages')) { + $receiverNames = array(); + foreach ($receiverMapping as $name => $reference) { + $receiverNames[(string) $reference] = $name; + } + $container->getDefinition('console.command.messenger_consume_messages')->replaceArgument(3, array_values($receiverNames)); } $container->getDefinition('messenger.receiver_locator')->replaceArgument(0, $receiverMapping); diff --git a/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php index 4eca2423aa47c..de489b545f0e4 100644 --- a/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php +++ b/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php @@ -20,15 +20,15 @@ class ConsumeMessagesCommandTest extends TestCase { public function testConfigurationWithDefaultReceiver() { - $command = new ConsumeMessagesCommand($this->createMock(MessageBus::class), $this->createMock(ServiceLocator::class), null, 'messenger.transport.amqp'); + $command = new ConsumeMessagesCommand($this->createMock(MessageBus::class), $this->createMock(ServiceLocator::class), null, array('amqp')); $inputArgument = $command->getDefinition()->getArgument('receiver'); $this->assertFalse($inputArgument->isRequired()); - $this->assertSame('messenger.transport.amqp', $inputArgument->getDefault()); + $this->assertSame('amqp', $inputArgument->getDefault()); } public function testConfigurationWithoutDefaultReceiver() { - $command = new ConsumeMessagesCommand($this->createMock(MessageBus::class), $this->createMock(ServiceLocator::class)); + $command = new ConsumeMessagesCommand($this->createMock(MessageBus::class), $this->createMock(ServiceLocator::class), null, array('amqp', 'dummy')); $inputArgument = $command->getDefinition()->getArgument('receiver'); $this->assertTrue($inputArgument->isRequired()); $this->assertNull($inputArgument->getDefault()); diff --git a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php index d49a38421b1d3..5e35b18590ca5 100644 --- a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php +++ b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php @@ -118,24 +118,7 @@ public function testItRegistersReceiversWithoutTagName() $this->assertEquals(array(AmqpReceiver::class => new Reference(AmqpReceiver::class)), $container->getDefinition('messenger.receiver_locator')->getArgument(0)); } - public function testItRegistersOneReceiverAndSetsTheDefaultOneOnTheCommand() - { - $container = $this->getContainerBuilder(); - $container->register('console.command.messenger_consume_messages', ConsumeMessagesCommand::class)->setArguments(array( - new Reference('message_bus'), - new Reference('messenger.receiver_locator'), - null, - null, - )); - - $container->register(AmqpReceiver::class, AmqpReceiver::class)->addTag('messenger.receiver', array('alias' => 'amqp')); - - (new MessengerPass())->process($container); - - $this->assertSame(AmqpReceiver::class, $container->getDefinition('console.command.messenger_consume_messages')->getArgument(3)); - } - - public function testItRegistersMultipleReceiversAndDoesNotSetTheDefaultOneOnTheCommand() + public function testItRegistersMultipleReceiversAndSetsTheReceiverNamesOnTheCommand() { $container = $this->getContainerBuilder(); $container->register('console.command.messenger_consume_messages', ConsumeMessagesCommand::class)->setArguments(array( @@ -150,7 +133,7 @@ public function testItRegistersMultipleReceiversAndDoesNotSetTheDefaultOneOnTheC (new MessengerPass())->process($container); - $this->assertNull($container->getDefinition('console.command.messenger_consume_messages')->getArgument(3)); + $this->assertSame(array('amqp', 'dummy'), $container->getDefinition('console.command.messenger_consume_messages')->getArgument(3)); } public function testItRegistersSenders() From 3381611d86e684737c064efef0d937e08a801a63 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 10 May 2018 19:25:00 -0700 Subject: [PATCH 35/76] [FrameworkBundle] Fix cache:clear on vagrant --- .../Command/CacheClearCommand.php | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php index bbb71c15dd92a..650f36dc15b71 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php @@ -140,16 +140,34 @@ protected function execute(InputInterface $input, OutputInterface $output) } } - $containerDir = $fs->exists($warmupDir.'/'.$containerDir) ? false : $containerDir; + if (!$fs->exists($warmupDir.'/'.$containerDir)) { + $fs->rename($realCacheDir.'/'.$containerDir, $warmupDir.'/'.$containerDir); + touch($warmupDir.'/'.$containerDir.'.legacy'); + } - $fs->rename($realCacheDir, $oldCacheDir); - $fs->rename($warmupDir, $realCacheDir); + if ('/' === \DIRECTORY_SEPARATOR && $mounts = @file('/proc/mounts')) { + foreach ($mounts as $mount) { + $mount = array_slice(explode(' ', $mount), 1, -3); + if (!\in_array(array_pop($mount), array('vboxfs', 'nfs'))) { + continue; + } + $mount = implode(' ', $mount).'/'; - if ($containerDir) { - $fs->rename($oldCacheDir.'/'.$containerDir, $realCacheDir.'/'.$containerDir); - touch($realCacheDir.'/'.$containerDir.'.legacy'); + if (0 === strpos($realCacheDir, $mount)) { + $io->note('For better performances, you should move the cache and log directories to a non-shared folder of the VM.'); + $oldCacheDir = false; + break; + } + } } + if ($oldCacheDir) { + $fs->rename($realCacheDir, $oldCacheDir); + } else { + $fs->remove($realCacheDir); + } + $fs->rename($warmupDir, $realCacheDir); + if ($output->isVerbose()) { $io->comment('Removing old cache directory...'); } From 8072eed4bf6237df15f74647c793cf2e951b6ab8 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 11 May 2018 17:48:19 +0200 Subject: [PATCH 36/76] fixed CS --- .../Handler/FingersCrossed/NotFoundActivationStrategy.php | 2 +- src/Symfony/Component/Finder/Shell/Command.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/NotFoundActivationStrategy.php b/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/NotFoundActivationStrategy.php index 413b476f2938d..596fcdd84d2c5 100644 --- a/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/NotFoundActivationStrategy.php +++ b/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/NotFoundActivationStrategy.php @@ -42,7 +42,7 @@ public function isHandlerActivated(array $record) $isActivated && isset($record['context']['exception']) && $record['context']['exception'] instanceof HttpException - && $record['context']['exception']->getStatusCode() == 404 + && 404 == $record['context']['exception']->getStatusCode() && ($request = $this->requestStack->getMasterRequest()) ) { return !preg_match($this->blacklist, $request->getPathInfo()); diff --git a/src/Symfony/Component/Finder/Shell/Command.php b/src/Symfony/Component/Finder/Shell/Command.php index 47f4b422216b9..43114dbcc07db 100644 --- a/src/Symfony/Component/Finder/Shell/Command.php +++ b/src/Symfony/Component/Finder/Shell/Command.php @@ -100,7 +100,7 @@ public function top($bit) array_unshift($this->bits, $bit); foreach ($this->labels as $label => $index) { - $this->labels[$label] += 1; + ++$this->labels[$label]; } return $this; From df43c1e99dea042c9911fd6dd640ba9fd9bea3f3 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 11 May 2018 17:51:28 +0200 Subject: [PATCH 37/76] fixed CS --- .../SecurityBundle/DependencyInjection/SecurityExtension.php | 1 - .../Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 91d2d979c16b1..7ebe982e6d0b3 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -14,7 +14,6 @@ use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; -use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\HttpKernel\DependencyInjection\Extension; diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php index 2c4c15f410428..44f6e45e924cf 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php @@ -2568,7 +2568,7 @@ public function testStripLeadingUnderscoresAndDigitsFromId() $this->assertEquals('_09name', $view->vars['full_name']); } - /** + /** * @dataProvider provideTrimCases */ public function testTrimIsDisabled($multiple, $expanded) From 4f3afd53a8e3c673b889eb936410291108c89d88 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 11 May 2018 17:58:37 +0200 Subject: [PATCH 38/76] fixed CS --- .../PhpUnit/Legacy/CoverageListenerTrait.php | 1 - .../weak_vendors_on_eval_d_deprecation.phpt | 2 +- src/Symfony/Bridge/Twig/NodeVisitor/Scope.php | 2 +- .../Tests/Controller/ControllerTest.php | 1 - .../Cache/DataCollector/CacheDataCollector.php | 18 +++++++++--------- .../Component/CssSelector/Node/Specificity.php | 4 ++-- .../Component/CssSelector/XPath/Translator.php | 4 ++-- .../Component/CssSelector/XPath/XPathExpr.php | 6 +++--- .../Debug/Exception/FlattenException.php | 2 +- .../Configurator/InstanceofConfigurator.php | 2 +- src/Symfony/Component/Form/FormInterface.php | 2 +- src/Symfony/Component/Form/FormView.php | 2 +- .../Tests/AbstractBootstrap4LayoutTest.php | 2 +- .../Storage/Handler/PdoSessionHandler.php | 2 +- .../HttpFoundation/Tests/ResponseTest.php | 2 +- .../Component/HttpKernel/Profiler/Profile.php | 4 ++-- .../ContainerControllerResolverTest.php | 1 - .../ArgumentMetadataFactoryTest.php | 4 ++-- .../HttpKernel/Tests/Profiler/ProfilerTest.php | 2 +- src/Symfony/Component/Ldap/Ldap.php | 2 +- .../OptionsResolver/OptionsResolver.php | 2 +- .../PropertyAccess/PropertyAccessor.php | 2 +- src/Symfony/Component/PropertyInfo/Type.php | 2 +- .../Matcher/Dumper/DumperCollection.php | 2 +- .../Routing/RouteCollectionBuilder.php | 4 ++-- .../Firewall/GuardAuthenticationListener.php | 1 - .../Security/Http/Firewall/ContextListener.php | 4 ++-- .../Mapping/AttributeMetadataInterface.php | 2 +- .../Mapping/ClassMetadataInterface.php | 2 +- .../Normalizer/AbstractObjectNormalizer.php | 4 ++-- .../Translation/MessageCatalogueInterface.php | 4 ++-- .../ConstraintViolationListInterface.php | 2 +- .../Validator/Mapping/ClassMetadata.php | 2 +- 33 files changed, 47 insertions(+), 51 deletions(-) diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerTrait.php b/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerTrait.php index 1c84ff32c0b74..ab1cda6702be5 100644 --- a/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerTrait.php +++ b/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerTrait.php @@ -11,7 +11,6 @@ namespace Symfony\Bridge\PhpUnit\Legacy; -use PHPUnit\Framework\Test; use PHPUnit\Framework\TestCase; use PHPUnit\Framework\Warning; diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_eval_d_deprecation.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_eval_d_deprecation.phpt index 8fa436e20178b..8390d16332fa1 100644 --- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_eval_d_deprecation.phpt +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_eval_d_deprecation.phpt @@ -15,7 +15,7 @@ while (!file_exists($vendor.'/vendor')) { define('PHPUNIT_COMPOSER_INSTALL', $vendor.'/vendor/autoload.php'); require PHPUNIT_COMPOSER_INSTALL; require_once __DIR__.'/../../bootstrap.php'; -eval("@trigger_error('who knows where I come from?', E_USER_DEPRECATED);") +eval("@trigger_error('who knows where I come from?', E_USER_DEPRECATED);"); ?> --EXPECTF-- diff --git a/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php b/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php index 1c3451bbebf46..59497dc961984 100644 --- a/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php +++ b/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php @@ -20,7 +20,7 @@ class Scope private $data = array(); private $left = false; - public function __construct(Scope $parent = null) + public function __construct(self $parent = null) { $this->parent = $parent; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTest.php index 9fd20649289de..452845cea8e34 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTest.php @@ -12,7 +12,6 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; -use Symfony\Component\HttpFoundation\File\File; class ControllerTest extends ControllerTraitTest { diff --git a/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php b/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php index 14c6a8d0fa584..91763e5a9f33b 100644 --- a/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php +++ b/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php @@ -119,30 +119,30 @@ private function calculateStatistics(): array ); /** @var TraceableAdapterEvent $call */ foreach ($calls as $call) { - $statistics[$name]['calls'] += 1; + ++$statistics[$name]['calls']; $statistics[$name]['time'] += $call->end - $call->start; if ('getItem' === $call->name) { - $statistics[$name]['reads'] += 1; + ++$statistics[$name]['reads']; if ($call->hits) { - $statistics[$name]['hits'] += 1; + ++$statistics[$name]['hits']; } else { - $statistics[$name]['misses'] += 1; + ++$statistics[$name]['misses']; } } elseif ('getItems' === $call->name) { $statistics[$name]['reads'] += $call->hits + $call->misses; $statistics[$name]['hits'] += $call->hits; $statistics[$name]['misses'] += $call->misses; } elseif ('hasItem' === $call->name) { - $statistics[$name]['reads'] += 1; + ++$statistics[$name]['reads']; if (false === $call->result) { - $statistics[$name]['misses'] += 1; + ++$statistics[$name]['misses']; } else { - $statistics[$name]['hits'] += 1; + ++$statistics[$name]['hits']; } } elseif ('save' === $call->name) { - $statistics[$name]['writes'] += 1; + ++$statistics[$name]['writes']; } elseif ('deleteItem' === $call->name) { - $statistics[$name]['deletes'] += 1; + ++$statistics[$name]['deletes']; } } if ($statistics[$name]['reads']) { diff --git a/src/Symfony/Component/CssSelector/Node/Specificity.php b/src/Symfony/Component/CssSelector/Node/Specificity.php index 11228f7246d3a..9b35cb411632c 100644 --- a/src/Symfony/Component/CssSelector/Node/Specificity.php +++ b/src/Symfony/Component/CssSelector/Node/Specificity.php @@ -40,7 +40,7 @@ public function __construct(int $a, int $b, int $c) $this->c = $c; } - public function plus(Specificity $specificity): Specificity + public function plus(self $specificity): self { return new self($this->a + $specificity->a, $this->b + $specificity->b, $this->c + $specificity->c); } @@ -56,7 +56,7 @@ public function getValue(): int * * @return int */ - public function compareTo(Specificity $specificity) + public function compareTo(self $specificity) { if ($this->a !== $specificity->a) { return $this->a > $specificity->a ? 1 : -1; diff --git a/src/Symfony/Component/CssSelector/XPath/Translator.php b/src/Symfony/Component/CssSelector/XPath/Translator.php index 28478b3db7e47..73b548215d671 100644 --- a/src/Symfony/Component/CssSelector/XPath/Translator.php +++ b/src/Symfony/Component/CssSelector/XPath/Translator.php @@ -114,7 +114,7 @@ public function selectorToXPath(SelectorNode $selector, string $prefix = 'descen return ($prefix ?: '').$this->nodeToXPath($selector); } - public function registerExtension(Extension\ExtensionInterface $extension): Translator + public function registerExtension(Extension\ExtensionInterface $extension): self { $this->extensions[$extension->getName()] = $extension; @@ -139,7 +139,7 @@ public function getExtension(string $name): Extension\ExtensionInterface return $this->extensions[$name]; } - public function registerParserShortcut(ParserInterface $shortcut): Translator + public function registerParserShortcut(ParserInterface $shortcut): self { $this->shortcutParsers[] = $shortcut; diff --git a/src/Symfony/Component/CssSelector/XPath/XPathExpr.php b/src/Symfony/Component/CssSelector/XPath/XPathExpr.php index 8090df99075fa..638cbd0fe0619 100644 --- a/src/Symfony/Component/CssSelector/XPath/XPathExpr.php +++ b/src/Symfony/Component/CssSelector/XPath/XPathExpr.php @@ -43,7 +43,7 @@ public function getElement(): string return $this->element; } - public function addCondition(string $condition): XPathExpr + public function addCondition(string $condition): self { $this->condition = $this->condition ? sprintf('(%s) and (%s)', $this->condition, $condition) : $condition; @@ -55,7 +55,7 @@ public function getCondition(): string return $this->condition; } - public function addNameTest(): XPathExpr + public function addNameTest(): self { if ('*' !== $this->element) { $this->addCondition('name() = '.Translator::getXpathLiteral($this->element)); @@ -65,7 +65,7 @@ public function addNameTest(): XPathExpr return $this; } - public function addStarPrefix(): XPathExpr + public function addStarPrefix(): self { $this->path .= '*/'; diff --git a/src/Symfony/Component/Debug/Exception/FlattenException.php b/src/Symfony/Component/Debug/Exception/FlattenException.php index 24679dcaab242..f491bf2ac4c92 100644 --- a/src/Symfony/Component/Debug/Exception/FlattenException.php +++ b/src/Symfony/Component/Debug/Exception/FlattenException.php @@ -157,7 +157,7 @@ public function getPrevious() return $this->previous; } - public function setPrevious(FlattenException $previous) + public function setPrevious(self $previous) { $this->previous = $previous; } diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/InstanceofConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/InstanceofConfigurator.php index 9ecb2a21c3deb..78a8e3c327f85 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/InstanceofConfigurator.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/InstanceofConfigurator.php @@ -30,7 +30,7 @@ class InstanceofConfigurator extends AbstractServiceConfigurator /** * Defines an instanceof-conditional to be applied to following service definitions. */ - final public function instanceof(string $fqcn): InstanceofConfigurator + final public function instanceof(string $fqcn): self { return $this->parent->instanceof($fqcn); } diff --git a/src/Symfony/Component/Form/FormInterface.php b/src/Symfony/Component/Form/FormInterface.php index 921ae2829c5ff..a0e9b636c5f38 100644 --- a/src/Symfony/Component/Form/FormInterface.php +++ b/src/Symfony/Component/Form/FormInterface.php @@ -29,7 +29,7 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable * @throws Exception\LogicException when trying to set a parent for a form with * an empty name */ - public function setParent(FormInterface $parent = null); + public function setParent(self $parent = null); /** * Returns the parent form. diff --git a/src/Symfony/Component/Form/FormView.php b/src/Symfony/Component/Form/FormView.php index 21a3d5036ad2b..9fbca4379688a 100644 --- a/src/Symfony/Component/Form/FormView.php +++ b/src/Symfony/Component/Form/FormView.php @@ -51,7 +51,7 @@ class FormView implements \ArrayAccess, \IteratorAggregate, \Countable private $methodRendered = false; - public function __construct(FormView $parent = null) + public function __construct(self $parent = null) { $this->parent = $parent; } diff --git a/src/Symfony/Component/Form/Tests/AbstractBootstrap4LayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractBootstrap4LayoutTest.php index 2cadba0ba9e67..a2c606c0362ab 100644 --- a/src/Symfony/Component/Form/Tests/AbstractBootstrap4LayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractBootstrap4LayoutTest.php @@ -178,7 +178,7 @@ public function testErrors() public function testErrorWithNoLabel() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', array('label'=>false)); + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', array('label' => false)); $form->addError(new FormError('[trans]Error 1[/trans]')); $view = $form->createView(); $html = $this->renderLabel($view); diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php index 5ae3d52cc30b1..7b30aed381053 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php @@ -833,7 +833,7 @@ private function getUpdateStatement($sessionId, $sessionData, $maxlifetime) /** * Returns a merge/upsert (i.e. insert or update) statement when supported by the database for writing session data. */ - private function getMergeStatement(string $sessionId, string $data, int$maxlifetime): ?\PDOStatement + private function getMergeStatement(string $sessionId, string $data, int $maxlifetime): ?\PDOStatement { switch (true) { case 'mysql' === $this->driver: diff --git a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php index b35727962eacd..b4af82ddf68de 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php @@ -300,7 +300,7 @@ public function testGetMaxAge() $response = new Response(); $response->headers->set('Cache-Control', 'must-revalidate'); $response->headers->set('Expires', -1); - $this->assertLessThanOrEqual(time() - 2*86400, $response->getExpires()->format('U')); + $this->assertLessThanOrEqual(time() - 2 * 86400, $response->getExpires()->format('U')); $response = new Response(); $this->assertNull($response->getMaxAge(), '->getMaxAge() returns null if no freshness information available'); diff --git a/src/Symfony/Component/HttpKernel/Profiler/Profile.php b/src/Symfony/Component/HttpKernel/Profiler/Profile.php index e91a0b0c70e49..cc7efc8487475 100644 --- a/src/Symfony/Component/HttpKernel/Profiler/Profile.php +++ b/src/Symfony/Component/HttpKernel/Profiler/Profile.php @@ -71,7 +71,7 @@ public function getToken() /** * Sets the parent token. */ - public function setParent(Profile $parent) + public function setParent(self $parent) { $this->parent = $parent; } @@ -210,7 +210,7 @@ public function setChildren(array $children) /** * Adds the child token. */ - public function addChild(Profile $child) + public function addChild(self $child) { $this->children[] = $child; $child->setParent($this); diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php index 6f203d3a987c3..1602de4c261f9 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php @@ -13,7 +13,6 @@ use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; -use Symfony\Component\Debug\ErrorHandler; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ContainerControllerResolver; diff --git a/src/Symfony/Component/HttpKernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php b/src/Symfony/Component/HttpKernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php index 2ddc6e7a5ee10..a667705f50813 100644 --- a/src/Symfony/Component/HttpKernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php @@ -117,11 +117,11 @@ public function testNullableTypesSignature() ), $arguments); } - private function signature1(ArgumentMetadataFactoryTest $foo, array $bar, callable $baz) + private function signature1(self $foo, array $bar, callable $baz) { } - private function signature2(ArgumentMetadataFactoryTest $foo = null, FakeClassThatDoesNotExist $bar = null, ImportedAndFake $baz = null) + private function signature2(self $foo = null, FakeClassThatDoesNotExist $bar = null, ImportedAndFake $baz = null) { } diff --git a/src/Symfony/Component/HttpKernel/Tests/Profiler/ProfilerTest.php b/src/Symfony/Component/HttpKernel/Tests/Profiler/ProfilerTest.php index 243c3c5c5a7cb..2d5f0ca5ad59e 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Profiler/ProfilerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Profiler/ProfilerTest.php @@ -44,7 +44,7 @@ public function testCollect() public function testReset() { $collector = $this->getMockBuilder(DataCollectorInterface::class) - ->setMethods(['collect', 'getName', 'reset']) + ->setMethods(array('collect', 'getName', 'reset')) ->getMock(); $collector->expects($this->any())->method('getName')->willReturn('mock'); $collector->expects($this->once())->method('reset'); diff --git a/src/Symfony/Component/Ldap/Ldap.php b/src/Symfony/Component/Ldap/Ldap.php index 26fd4f946ccc9..de1ab1a249eda 100644 --- a/src/Symfony/Component/Ldap/Ldap.php +++ b/src/Symfony/Component/Ldap/Ldap.php @@ -70,7 +70,7 @@ public function escape($subject, $ignore = '', $flags = 0) * * @return static */ - public static function create($adapter, array $config = array()): Ldap + public static function create($adapter, array $config = array()): self { if (!isset(self::$adapterMap[$adapter])) { throw new DriverNotFoundException(sprintf( diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index 8edd6b5c89791..68b4154b10da2 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -975,7 +975,7 @@ public function count() * parameters should usually not be included in messages aimed at * non-technical people. * - * @param mixed $value The value to return the type of + * @param mixed $value The value to return the type of */ private function formatTypeOf($value, ?string $type): string { diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 1768dda885421..40a2ae047e812 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -588,7 +588,7 @@ private function writeCollection($zval, $property, $collection, $addMethod, $rem /** * Guesses how to write the property value. * - * @param mixed $value + * @param mixed $value */ private function getWriteAccessInfo(string $class, string $property, $value): array { diff --git a/src/Symfony/Component/PropertyInfo/Type.php b/src/Symfony/Component/PropertyInfo/Type.php index 71aa162f70b79..461495ed510ac 100644 --- a/src/Symfony/Component/PropertyInfo/Type.php +++ b/src/Symfony/Component/PropertyInfo/Type.php @@ -59,7 +59,7 @@ class Type /** * @throws \InvalidArgumentException */ - public function __construct(string $builtinType, bool $nullable = false, string $class = null, bool $collection = false, Type $collectionKeyType = null, Type $collectionValueType = null) + public function __construct(string $builtinType, bool $nullable = false, string $class = null, bool $collection = false, self $collectionKeyType = null, self $collectionValueType = null) { if (!in_array($builtinType, self::$builtinTypes)) { throw new \InvalidArgumentException(sprintf('"%s" is not a valid PHP type.', $builtinType)); diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php b/src/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php index 6916297b8c174..dd057d2ee8301 100644 --- a/src/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php +++ b/src/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php @@ -106,7 +106,7 @@ protected function getParent() /** * Sets the parent collection. */ - protected function setParent(DumperCollection $parent) + protected function setParent(self $parent) { $this->parent = $parent; } diff --git a/src/Symfony/Component/Routing/RouteCollectionBuilder.php b/src/Symfony/Component/Routing/RouteCollectionBuilder.php index d63c6138f7983..a58613cefe48f 100644 --- a/src/Symfony/Component/Routing/RouteCollectionBuilder.php +++ b/src/Symfony/Component/Routing/RouteCollectionBuilder.php @@ -118,7 +118,7 @@ public function createBuilder() * @param string $prefix * @param RouteCollectionBuilder $builder */ - public function mount($prefix, RouteCollectionBuilder $builder) + public function mount($prefix, self $builder) { $builder->prefix = trim(trim($prefix), '/'); $this->routes[] = $builder; @@ -255,7 +255,7 @@ public function setMethods($methods) * * @return $this */ - private function addResource(ResourceInterface $resource): RouteCollectionBuilder + private function addResource(ResourceInterface $resource): self { $this->resources[] = $resource; diff --git a/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php b/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php index 80ae75caa2b85..b9859452b1585 100644 --- a/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php +++ b/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php @@ -14,7 +14,6 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\GetResponseEvent; -use Symfony\Component\Security\Guard\AbstractGuardAuthenticator; use Symfony\Component\Security\Guard\GuardAuthenticatorHandler; use Symfony\Component\Security\Guard\AuthenticatorInterface; use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken; diff --git a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php index fb8c7a1761cbc..00440702279a7 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php @@ -45,8 +45,8 @@ class ContextListener implements ListenerInterface private $trustResolver; /** - * @param TokenStorageInterface $tokenStorage - * @param iterable|UserProviderInterface[] $userProviders + * @param TokenStorageInterface $tokenStorage + * @param iterable|UserProviderInterface[] $userProviders */ public function __construct(TokenStorageInterface $tokenStorage, iterable $userProviders, string $contextKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, AuthenticationTrustResolverInterface $trustResolver = null) { diff --git a/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php b/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php index 944a7b3133f35..d9a15d5ac0de5 100644 --- a/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php +++ b/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php @@ -60,5 +60,5 @@ public function getMaxDepth(); /** * Merges an {@see AttributeMetadataInterface} with in the current one. */ - public function merge(AttributeMetadataInterface $attributeMetadata); + public function merge(self $attributeMetadata); } diff --git a/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php b/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php index 3811e56548a0c..a0765861643bb 100644 --- a/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php +++ b/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php @@ -46,7 +46,7 @@ public function getAttributesMetadata(); /** * Merges a {@link ClassMetadataInterface} in the current one. */ - public function merge(ClassMetadataInterface $classMetadata); + public function merge(self $classMetadata); /** * Returns a {@link \ReflectionClass} instance for this class. diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index ecf60dc8884a2..e2b15d1aa1c41 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -228,7 +228,7 @@ abstract protected function setAttributeValue($object, $attribute, $value, $form /** * Validates the submitted data and denormalizes it. * - * @param mixed $data + * @param mixed $data * * @return mixed * @@ -297,7 +297,7 @@ private function validateAndDenormalize(string $currentClass, string $attribute, /** * Sets an attribute and apply the name converter if necessary. * - * @param mixed $attributeValue + * @param mixed $attributeValue */ private function updateData(array $data, string $attribute, $attributeValue): array { diff --git a/src/Symfony/Component/Translation/MessageCatalogueInterface.php b/src/Symfony/Component/Translation/MessageCatalogueInterface.php index 4dad27fbf6aa3..e0dbb2bd962cb 100644 --- a/src/Symfony/Component/Translation/MessageCatalogueInterface.php +++ b/src/Symfony/Component/Translation/MessageCatalogueInterface.php @@ -105,7 +105,7 @@ public function add($messages, $domain = 'messages'); * * The two catalogues must have the same locale. */ - public function addCatalogue(MessageCatalogueInterface $catalogue); + public function addCatalogue(self $catalogue); /** * Merges translations from the given Catalogue into the current one @@ -113,7 +113,7 @@ public function addCatalogue(MessageCatalogueInterface $catalogue); * * This is used to provide default translations when they do not exist for the current locale. */ - public function addFallbackCatalogue(MessageCatalogueInterface $catalogue); + public function addFallbackCatalogue(self $catalogue); /** * Gets the fallback catalogue. diff --git a/src/Symfony/Component/Validator/ConstraintViolationListInterface.php b/src/Symfony/Component/Validator/ConstraintViolationListInterface.php index 0489ab500a1dc..47e986f51e50a 100644 --- a/src/Symfony/Component/Validator/ConstraintViolationListInterface.php +++ b/src/Symfony/Component/Validator/ConstraintViolationListInterface.php @@ -26,7 +26,7 @@ public function add(ConstraintViolationInterface $violation); /** * Merges an existing violation list into this list. */ - public function addAll(ConstraintViolationListInterface $otherList); + public function addAll(self $otherList); /** * Returns the violation at a given offset. diff --git a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php index dd0dd1fa46a97..d7773861445fb 100644 --- a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php +++ b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php @@ -330,7 +330,7 @@ public function addGetterMethodConstraints($property, $method, array $constraint /** * Merges the constraints of the given metadata into this object. */ - public function mergeConstraints(ClassMetadata $source) + public function mergeConstraints(self $source) { if ($source->isGroupSequenceProvider()) { $this->setGroupSequenceProvider(true); From 2882f8d8c848ebbc919d247662c5cb4eb8301f78 Mon Sep 17 00:00:00 2001 From: Valentin Date: Mon, 7 May 2018 23:13:10 +0300 Subject: [PATCH 39/76] [Workflow] Added DefinitionBuilder::setMetadataStore(). --- src/Symfony/Component/Workflow/Definition.php | 1 - .../Component/Workflow/DefinitionBuilder.php | 16 +++++++++++++++- .../Workflow/Tests/DefinitionBuilderTest.php | 11 +++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Workflow/Definition.php b/src/Symfony/Component/Workflow/Definition.php index 9e9e1e796fcce..310cec8f15467 100644 --- a/src/Symfony/Component/Workflow/Definition.php +++ b/src/Symfony/Component/Workflow/Definition.php @@ -30,7 +30,6 @@ final class Definition /** * @param string[] $places * @param Transition[] $transitions - * @param string|null $initialPlace */ public function __construct(array $places, array $transitions, string $initialPlace = null, MetadataStoreInterface $metadataStore = null) { diff --git a/src/Symfony/Component/Workflow/DefinitionBuilder.php b/src/Symfony/Component/Workflow/DefinitionBuilder.php index 94e1e2effe16f..308f950324135 100644 --- a/src/Symfony/Component/Workflow/DefinitionBuilder.php +++ b/src/Symfony/Component/Workflow/DefinitionBuilder.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Workflow; +use Symfony\Component\Workflow\Metadata\MetadataStoreInterface; + /** * Builds a definition. * @@ -23,6 +25,7 @@ class DefinitionBuilder private $places = array(); private $transitions = array(); private $initialPlace; + private $metadataStore; /** * @param string[] $places @@ -39,7 +42,7 @@ public function __construct(array $places = array(), array $transitions = array( */ public function build() { - return new Definition($this->places, $this->transitions, $this->initialPlace); + return new Definition($this->places, $this->transitions, $this->initialPlace, $this->metadataStore); } /** @@ -52,6 +55,7 @@ public function clear() $this->places = array(); $this->transitions = array(); $this->initialPlace = null; + $this->metadataStore = null; return $this; } @@ -122,6 +126,16 @@ public function addTransition(Transition $transition) return $this; } + /** + * @return $this + */ + public function setMetadataStore(MetadataStoreInterface $metadataStore) + { + $this->metadataStore = $metadataStore; + + return $this; + } + /** * @deprecated since Symfony 4.1, use the clear() method instead. * diff --git a/src/Symfony/Component/Workflow/Tests/DefinitionBuilderTest.php b/src/Symfony/Component/Workflow/Tests/DefinitionBuilderTest.php index 1939fb5713963..1af7a326935d6 100644 --- a/src/Symfony/Component/Workflow/Tests/DefinitionBuilderTest.php +++ b/src/Symfony/Component/Workflow/Tests/DefinitionBuilderTest.php @@ -4,6 +4,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Workflow\DefinitionBuilder; +use Symfony\Component\Workflow\Metadata\InMemoryMetadataStore; use Symfony\Component\Workflow\Transition; class DefinitionBuilderTest extends TestCase @@ -44,4 +45,14 @@ public function testAddPlace() $this->assertEquals('a', $definition->getPlaces()['a']); $this->assertEquals('b', $definition->getPlaces()['b']); } + + public function testSetMetadataStore() + { + $builder = new DefinitionBuilder(array('a')); + $metadataStore = new InMemoryMetadataStore(); + $builder->setMetadataStore($metadataStore); + $definition = $builder->build(); + + $this->assertSame($metadataStore, $definition->getMetadataStore()); + } } From d7e612d2acda29f18d187167c8dbe8e260423b2e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 11 May 2018 10:00:11 -0700 Subject: [PATCH 40/76] [Debug] Fix populating error_get_last() for handled silent errors --- src/Symfony/Component/Debug/ErrorHandler.php | 8 ++++--- .../Debug/Tests/ErrorHandlerTest.php | 24 +++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php index e056862b8e675..4671b85469d78 100644 --- a/src/Symfony/Component/Debug/ErrorHandler.php +++ b/src/Symfony/Component/Debug/ErrorHandler.php @@ -377,13 +377,15 @@ private function reRegister($prev) */ public function handleError($type, $message, $file, $line) { - $level = error_reporting() | E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED; + $level = error_reporting(); + $silenced = 0 === ($level & $type); + $level |= E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED; $log = $this->loggedErrors & $type; $throw = $this->thrownErrors & $type & $level; $type &= $level | $this->screamedErrors; if (!$type || (!$log && !$throw)) { - return $type && $log; + return !$silenced && $type && $log; } $scope = $this->scopedErrors & $type; @@ -479,7 +481,7 @@ public function handleError($type, $message, $file, $line) } } - return $type && $log; + return !$silenced && $type && $log; } /** diff --git a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php index 4eb4aef0c0fa9..d8f4a74d86969 100644 --- a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php +++ b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php @@ -64,6 +64,30 @@ public function testRegister() } } + public function testErrorGetLast() + { + $handler = ErrorHandler::register(); + $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); + $handler->setDefaultLogger($logger); + $handler->screamAt(E_ALL); + + try { + @trigger_error('Hello', E_USER_WARNING); + $expected = array( + 'type' => E_USER_WARNING, + 'message' => 'Hello', + 'file' => __FILE__, + 'line' => __LINE__ - 5, + ); + $this->assertSame($expected, error_get_last()); + } catch (\Exception $e) { + restore_error_handler(); + restore_exception_handler(); + + throw $e; + } + } + public function testNotice() { ErrorHandler::register(); From 7904784a94b2715eca600317f5a1c18cd3eaed4a Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 10 May 2018 19:25:00 -0700 Subject: [PATCH 41/76] [Cache][Lock] Fix usages of error_get_last() --- src/Symfony/Component/Cache/Traits/RedisTrait.php | 9 ++++++--- src/Symfony/Component/Lock/Store/FlockStore.php | 6 ++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/Cache/Traits/RedisTrait.php b/src/Symfony/Component/Cache/Traits/RedisTrait.php index b8e05d9e417c9..9fc6177c6bd78 100644 --- a/src/Symfony/Component/Cache/Traits/RedisTrait.php +++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php @@ -126,9 +126,12 @@ public static function createConnection($dsn, array $options = array()) throw new InvalidArgumentException(sprintf('Redis connection failed (%s): %s', $e->getMessage(), $dsn)); } - if (@!$redis->isConnected()) { - $e = ($e = error_get_last()) && preg_match('/^Redis::p?connect\(\): (.*)/', $e['message'], $e) ? sprintf(' (%s)', $e[1]) : ''; - throw new InvalidArgumentException(sprintf('Redis connection failed%s: %s', $e, $dsn)); + set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; }); + $isConnected = $redis->isConnected(); + restore_error_handler(); + if (!$isConnected) { + $error = preg_match('/^Redis::p?connect\(\): (.*)/', $error, $error) ? sprintf(' (%s)', $error[1]) : ''; + throw new InvalidArgumentException(sprintf('Redis connection failed%s: %s', $error, $dsn)); } if ((null !== $auth && !$redis->auth($auth)) diff --git a/src/Symfony/Component/Lock/Store/FlockStore.php b/src/Symfony/Component/Lock/Store/FlockStore.php index 287d6fb583160..d0317358e89a5 100644 --- a/src/Symfony/Component/Lock/Store/FlockStore.php +++ b/src/Symfony/Component/Lock/Store/FlockStore.php @@ -78,8 +78,7 @@ private function lock(Key $key, $blocking) ); // Silence error reporting - set_error_handler(function () { - }); + set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; }); if (!$handle = fopen($fileName, 'r')) { if ($handle = fopen($fileName, 'x')) { chmod($fileName, 0444); @@ -91,8 +90,7 @@ private function lock(Key $key, $blocking) restore_error_handler(); if (!$handle) { - $error = error_get_last(); - throw new LockStorageException($error['message'], 0, null); + throw new LockStorageException($error, 0, null); } // On Windows, even if PHP doc says the contrary, LOCK_NB works, see From 3d19578297e64497d3613f9dc2294d510885a466 Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Tue, 8 May 2018 18:54:52 +0200 Subject: [PATCH 42/76] [Messenger] Improve the profiler panel --- .../views/Collector/messenger.html.twig | 171 ++++++++++++++---- .../views/Profiler/profiler.css.twig | 3 + .../DataCollector/MessengerDataCollector.php | 66 ++++--- .../MessengerDataCollectorTest.php | 88 +++++++-- .../Tests/Fixtures/AnEnvelopeItem.php | 33 ++++ .../Messenger/Tests/MessageBusTest.php | 21 +-- .../Tests/TraceableMessageBusTest.php | 24 ++- .../Messenger/TraceableMessageBus.php | 6 + 8 files changed, 310 insertions(+), 102 deletions(-) create mode 100644 src/Symfony/Component/Messenger/Tests/Fixtures/AnEnvelopeItem.php diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig index 908efe8771016..d8befbbf8dca6 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig @@ -4,17 +4,33 @@ {% block toolbar %} {% if collector.messages|length > 0 %} + {% set status_color = collector.exceptionsCount ? 'red' %} {% set icon %} {{ include('@WebProfiler/Icon/messenger.svg') }} {{ collector.messages|length }} {% endset %} - {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: 'messenger' }) }} + {% set text %} + {% for bus in collector.buses %} + {% set exceptionsCount = collector.exceptionsCount(bus) %} +
+ {{ bus }} + + {{ collector.messages(bus)|length }} + +
+ {% endfor %} + {% endset %} + + {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: 'messenger', status: status_color }) }} {% endif %} {% endblock %} {% block menu %} - + {{ include('@WebProfiler/Icon/messenger.svg') }} Messages @@ -24,7 +40,28 @@ {% endblock %} +{% block head %} + {{ parent() }} + +{% endblock %} + {% block panel %} + {% import _self as helper %} +

Messages

{% if collector.messages is empty %} @@ -32,41 +69,99 @@

No messages have been collected.

{% else %} - - - - - - - - - - {% for message in collector.messages %} - - - - - - {% endfor %} - -
BusMessageResult
{{ message.bus }} - {% if message.result.object is defined %} - {{ profiler_dump(message.message.object, maxDepth=2) }} - {% else %} - {{ message.message.type }} - {% endif %} - - {% if message.result.object is defined %} - {{ profiler_dump(message.result.object, maxDepth=2) }} - {% elseif message.result.type is defined %} - {{ message.result.type }} - {% if message.result.value is defined %} - {{ message.result.value }} - {% endif %} - {% endif %} - {% if message.exception.type is defined %} - {{ message.exception.type }} - {% endif %} -
+ +
+
+ {% set messages = collector.messages %} + {% set exceptionsCount = collector.exceptionsCount %} +

All{{ messages|length }}

+ +
+

Ordered list of dispatched messages across all your buses

+ {{ helper.render_bus_messages(messages, true) }} +
+
+ + {% for bus in collector.buses %} +
+ {% set messages = collector.messages(bus) %} + {% set exceptionsCount = collector.exceptionsCount(bus) %} +

{{ bus }}{{ messages|length }}

+ +
+

Ordered list of messages dispatched on the {{ bus }} bus

+ {{ helper.render_bus_messages(messages) }} +
+
+ {% endfor %} {% endif %} + {% endblock %} + +{% macro render_bus_messages(messages, showBus = false) %} + {% set discr = random() %} + {% for i, dispatchCall in messages %} + + + + + + + + {% if showBus %} + + + + + {% endif %} + + + + + + + + + + + + + {% if dispatchCall.exception is defined %} + + + + + {% endif %} + +
+ {{ profiler_dump(dispatchCall.message.type) }} + {% if showBus %} + {{ dispatchCall.bus }} + {% endif %} + {% if dispatchCall.exception is defined %} + exception + {% endif %} + + {{ include('@Twig/images/icon-minus-square.svg') }} + {{ include('@Twig/images/icon-plus-square.svg') }} + +
Bus{{ dispatchCall.bus }}
Message{{ profiler_dump(dispatchCall.message.value, maxDepth=2) }}
Envelope items + {% for item in dispatchCall.envelopeItems %} + {{ profiler_dump(item) }} + {% else %} + No items + {% endfor %} +
Result + {% if dispatchCall.result is defined %} + {{ profiler_dump(dispatchCall.result.seek('value'), maxDepth=2) }} + {% elseif dispatchCall.exception is defined %} + No result as an exception occurred + {% endif %} +
Exception + {{ profiler_dump(dispatchCall.exception.value, maxDepth=1) }} +
+ {% endfor %} +{% endmacro %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig index 96cd8878a8091..f9bc41d6a1b54 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig @@ -215,6 +215,9 @@ table tbody ul { .text-muted { color: #999; } +.text-danger { + color: {{ colors.error|raw }}; +} .text-bold { font-weight: bold; } diff --git a/src/Symfony/Component/Messenger/DataCollector/MessengerDataCollector.php b/src/Symfony/Component/Messenger/DataCollector/MessengerDataCollector.php index 0fe44d62fea16..ed98f4cfa43f5 100644 --- a/src/Symfony/Component/Messenger/DataCollector/MessengerDataCollector.php +++ b/src/Symfony/Component/Messenger/DataCollector/MessengerDataCollector.php @@ -16,6 +16,8 @@ use Symfony\Component\HttpKernel\DataCollector\DataCollector; use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface; use Symfony\Component\Messenger\TraceableMessageBus; +use Symfony\Component\VarDumper\Caster\ClassStub; +use Symfony\Component\VarDumper\Cloner\Data; /** * @author Samuel Roze @@ -44,13 +46,25 @@ public function collect(Request $request, Response $response, \Exception $except */ public function lateCollect() { - $this->data = array('messages' => array()); + $this->data = array('messages' => array(), 'buses' => array_keys($this->traceableBuses)); + $messages = array(); foreach ($this->traceableBuses as $busName => $bus) { foreach ($bus->getDispatchedMessages() as $message) { - $this->data['messages'][] = $this->collectMessage($busName, $message); + $debugRepresentation = $this->cloneVar($this->collectMessage($busName, $message)); + $messages[] = array($debugRepresentation, $message['callTime']); } } + + // Order by call time + usort($messages, function (array $a, array $b): int { + return $a[1] > $b[1] ? 1 : -1; + }); + + // Keep the messages clones only + $this->data['messages'] = array_map(function (array $item): Data { + return $item[0]; + }, $messages); } /** @@ -78,31 +92,19 @@ private function collectMessage(string $busName, array $tracedMessage) $debugRepresentation = array( 'bus' => $busName, + 'envelopeItems' => $tracedMessage['envelopeItems'] ?? null, 'message' => array( - 'type' => \get_class($message), - 'object' => $this->cloneVar($message), + 'type' => new ClassStub(\get_class($message)), + 'value' => $message, ), ); if (array_key_exists('result', $tracedMessage)) { $result = $tracedMessage['result']; - - if (\is_object($result)) { - $debugRepresentation['result'] = array( - 'type' => \get_class($result), - 'object' => $this->cloneVar($result), - ); - } elseif (\is_array($result)) { - $debugRepresentation['result'] = array( - 'type' => 'array', - 'object' => $this->cloneVar($result), - ); - } else { - $debugRepresentation['result'] = array( - 'type' => \gettype($result), - 'value' => $result, - ); - } + $debugRepresentation['result'] = array( + 'type' => \is_object($result) ? \get_class($result) : gettype($result), + 'value' => $result, + ); } if (isset($tracedMessage['exception'])) { @@ -110,15 +112,31 @@ private function collectMessage(string $busName, array $tracedMessage) $debugRepresentation['exception'] = array( 'type' => \get_class($exception), - 'message' => $exception->getMessage(), + 'value' => $exception, ); } return $debugRepresentation; } - public function getMessages(): array + public function getExceptionsCount(string $bus = null): int + { + return array_reduce($this->getMessages($bus), function (int $carry, Data $message) { + return $carry += isset($message['exception']) ? 1 : 0; + }, 0); + } + + public function getMessages(string $bus = null): array + { + $messages = $this->data['messages'] ?? array(); + + return $bus ? array_filter($messages, function (Data $message) use ($bus): bool { + return $bus === $message['bus']; + }) : $messages; + } + + public function getBuses(): array { - return $this->data['messages'] ?? array(); + return $this->data['buses']; } } diff --git a/src/Symfony/Component/Messenger/Tests/DataCollector/MessengerDataCollectorTest.php b/src/Symfony/Component/Messenger/Tests/DataCollector/MessengerDataCollectorTest.php index 91f54490ac9ec..d88593e3e747d 100644 --- a/src/Symfony/Component/Messenger/Tests/DataCollector/MessengerDataCollectorTest.php +++ b/src/Symfony/Component/Messenger/Tests/DataCollector/MessengerDataCollectorTest.php @@ -16,14 +16,22 @@ use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; use Symfony\Component\Messenger\TraceableMessageBus; -use Symfony\Component\VarDumper\Test\VarDumperTestTrait; +use Symfony\Component\VarDumper\Cloner\Data; +use Symfony\Component\VarDumper\Dumper\CliDumper; /** * @author Maxime Steinhausser */ class MessengerDataCollectorTest extends TestCase { - use VarDumperTestTrait; + /** @var CliDumper */ + private $dumper; + + protected function setUp() + { + $this->dumper = new CliDumper(); + $this->dumper->setColors(false); + } /** * @dataProvider getHandleTestData @@ -46,17 +54,18 @@ public function testHandle($returnedValue, $expected) $messages = $collector->getMessages(); $this->assertCount(1, $messages); - $this->assertDumpMatchesFormat($expected, $messages[0]); + $this->assertStringMatchesFormat($expected, $this->getDataAsString($messages[0])); } public function getHandleTestData() { $messageDump = << "default" + "envelopeItems" => null "message" => array:2 [ "type" => "Symfony\Component\Messenger\Tests\Fixtures\DummyMessage" - "object" => Symfony\Component\VarDumper\Cloner\Data {%A - %A+class: "Symfony\Component\Messenger\Tests\Fixtures\DummyMessage"%A + "value" => Symfony\Component\Messenger\Tests\Fixtures\DummyMessage %A + -message: "dummy message" } ] DUMP; @@ -64,7 +73,7 @@ public function getHandleTestData() yield 'no returned value' => array( null, << array:2 [ "type" => "NULL" @@ -77,7 +86,7 @@ public function getHandleTestData() yield 'scalar returned value' => array( 'returned value', << array:2 [ "type" => "string" @@ -90,11 +99,13 @@ public function getHandleTestData() yield 'array returned value' => array( array('returned value'), << array:2 [ "type" => "array" - "object" => Symfony\Component\VarDumper\Cloner\Data {%A + "value" => array:1 [ + 0 => "returned value" + ] ] ] DUMP @@ -123,21 +134,66 @@ public function testHandleWithException() $messages = $collector->getMessages(); $this->assertCount(1, $messages); - $this->assertDumpMatchesFormat(<<assertStringMatchesFormat(<< "default" + "envelopeItems" => null "message" => array:2 [ "type" => "Symfony\Component\Messenger\Tests\Fixtures\DummyMessage" - "object" => Symfony\Component\VarDumper\Cloner\Data {%A - %A+class: "Symfony\Component\Messenger\Tests\Fixtures\DummyMessage"%A + "value" => Symfony\Component\Messenger\Tests\Fixtures\DummyMessage %A + -message: "dummy message" } ] "exception" => array:2 [ "type" => "RuntimeException" - "message" => "foo" + "value" => RuntimeException %A ] -] +] DUMP - , $messages[0]); + , $this->getDataAsString($messages[0])); + } + + public function testKeepsOrderedDispatchCalls() + { + $firstBus = $this->getMockBuilder(MessageBusInterface::class)->getMock(); + $firstBus = new TraceableMessageBus($firstBus); + + $secondBus = $this->getMockBuilder(MessageBusInterface::class)->getMock(); + $secondBus = new TraceableMessageBus($secondBus); + + $collector = new MessengerDataCollector(); + $collector->registerBus('first bus', $firstBus); + $collector->registerBus('second bus', $secondBus); + + $firstBus->dispatch(new DummyMessage('#1')); + $secondBus->dispatch(new DummyMessage('#2')); + $secondBus->dispatch(new DummyMessage('#3')); + $firstBus->dispatch(new DummyMessage('#4')); + $secondBus->dispatch(new DummyMessage('#5')); + + $collector->lateCollect(); + + $messages = $collector->getMessages(); + $this->assertCount(5, $messages); + + $this->assertSame('#1', $messages[0]['message']['value']['message']); + $this->assertSame('first bus', $messages[0]['bus']); + + $this->assertSame('#2', $messages[1]['message']['value']['message']); + $this->assertSame('second bus', $messages[1]['bus']); + + $this->assertSame('#3', $messages[2]['message']['value']['message']); + $this->assertSame('second bus', $messages[2]['bus']); + + $this->assertSame('#4', $messages[3]['message']['value']['message']); + $this->assertSame('first bus', $messages[3]['bus']); + + $this->assertSame('#5', $messages[4]['message']['value']['message']); + $this->assertSame('second bus', $messages[4]['bus']); + } + + private function getDataAsString(Data $data): string + { + return rtrim($this->dumper->dump($data, true)); } } diff --git a/src/Symfony/Component/Messenger/Tests/Fixtures/AnEnvelopeItem.php b/src/Symfony/Component/Messenger/Tests/Fixtures/AnEnvelopeItem.php new file mode 100644 index 0000000000000..9e5bb0c92b63d --- /dev/null +++ b/src/Symfony/Component/Messenger/Tests/Fixtures/AnEnvelopeItem.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Tests\Fixtures; + +use Symfony\Component\Messenger\EnvelopeItemInterface; + +class AnEnvelopeItem implements EnvelopeItemInterface +{ + /** + * {@inheritdoc} + */ + public function serialize() + { + return ''; + } + + /** + * {@inheritdoc} + */ + public function unserialize($serialized) + { + // noop + } +} diff --git a/src/Symfony/Component/Messenger/Tests/MessageBusTest.php b/src/Symfony/Component/Messenger/Tests/MessageBusTest.php index ba40eb3f9a12c..9dc93a9d15213 100644 --- a/src/Symfony/Component/Messenger/Tests/MessageBusTest.php +++ b/src/Symfony/Component/Messenger/Tests/MessageBusTest.php @@ -15,10 +15,10 @@ use Symfony\Component\Messenger\Asynchronous\Transport\ReceivedMessage; use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\EnvelopeAwareInterface; -use Symfony\Component\Messenger\EnvelopeItemInterface; use Symfony\Component\Messenger\MessageBus; use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Messenger\Middleware\MiddlewareInterface; +use Symfony\Component\Messenger\Tests\Fixtures\AnEnvelopeItem; use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; class MessageBusTest extends TestCase @@ -160,22 +160,3 @@ public function testThatAMiddlewareCanUpdateTheMessageWhileKeepingTheEnvelopeIte $bus->dispatch($envelope); } } - -class AnEnvelopeItem implements EnvelopeItemInterface -{ - /** - * {@inheritdoc} - */ - public function serialize() - { - return ''; - } - - /** - * {@inheritdoc} - */ - public function unserialize($serialized) - { - return new self(); - } -} diff --git a/src/Symfony/Component/Messenger/Tests/TraceableMessageBusTest.php b/src/Symfony/Component/Messenger/Tests/TraceableMessageBusTest.php index 4da1e5a630202..8a2946ee42778 100644 --- a/src/Symfony/Component/Messenger/Tests/TraceableMessageBusTest.php +++ b/src/Symfony/Component/Messenger/Tests/TraceableMessageBusTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\MessageBusInterface; +use Symfony\Component\Messenger\Tests\Fixtures\AnEnvelopeItem; use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; use Symfony\Component\Messenger\TraceableMessageBus; @@ -28,19 +29,29 @@ public function testItTracesResult() $traceableBus = new TraceableMessageBus($bus); $this->assertSame($result, $traceableBus->dispatch($message)); - $this->assertSame(array(array('message' => $message, 'result' => $result)), $traceableBus->getDispatchedMessages()); + $this->assertCount(1, $tracedMessages = $traceableBus->getDispatchedMessages()); + $this->assertArraySubset(array( + 'message' => $message, + 'result' => $result, + 'envelopeItems' => null, + ), $tracedMessages[0], true); } public function testItTracesResultWithEnvelope() { - $envelope = Envelope::wrap($message = new DummyMessage('Hello')); + $envelope = Envelope::wrap($message = new DummyMessage('Hello'))->with($envelopeItem = new AnEnvelopeItem()); $bus = $this->getMockBuilder(MessageBusInterface::class)->getMock(); $bus->expects($this->once())->method('dispatch')->with($envelope)->willReturn($result = array('foo' => 'bar')); $traceableBus = new TraceableMessageBus($bus); $this->assertSame($result, $traceableBus->dispatch($envelope)); - $this->assertSame(array(array('message' => $message, 'result' => $result)), $traceableBus->getDispatchedMessages()); + $this->assertCount(1, $tracedMessages = $traceableBus->getDispatchedMessages()); + $this->assertArraySubset(array( + 'message' => $message, + 'result' => $result, + 'envelopeItems' => array($envelopeItem), + ), $tracedMessages[0], true); } public function testItTracesExceptions() @@ -58,6 +69,11 @@ public function testItTracesExceptions() $this->assertSame($exception, $e); } - $this->assertSame(array(array('message' => $message, 'exception' => $exception)), $traceableBus->getDispatchedMessages()); + $this->assertCount(1, $tracedMessages = $traceableBus->getDispatchedMessages()); + $this->assertArraySubset(array( + 'message' => $message, + 'exception' => $exception, + 'envelopeItems' => null, + ), $tracedMessages[0], true); } } diff --git a/src/Symfony/Component/Messenger/TraceableMessageBus.php b/src/Symfony/Component/Messenger/TraceableMessageBus.php index 9620e0189daca..b60d220b15ce1 100644 --- a/src/Symfony/Component/Messenger/TraceableMessageBus.php +++ b/src/Symfony/Component/Messenger/TraceableMessageBus.php @@ -29,21 +29,27 @@ public function __construct(MessageBusInterface $decoratedBus) */ public function dispatch($message) { + $callTime = microtime(true); $messageToTrace = $message instanceof Envelope ? $message->getMessage() : $message; + $envelopeItems = $message instanceof Envelope ? array_values($message->all()) : null; try { $result = $this->decoratedBus->dispatch($message); $this->dispatchedMessages[] = array( + 'envelopeItems' => $envelopeItems, 'message' => $messageToTrace, 'result' => $result, + 'callTime' => $callTime, ); return $result; } catch (\Throwable $e) { $this->dispatchedMessages[] = array( + 'envelopeItems' => $envelopeItems, 'message' => $messageToTrace, 'exception' => $e, + 'callTime' => $callTime, ); throw $e; From f8cde70ba19cb0ac547ea1e63f7a7aea2e07d4bd Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 13 May 2018 13:51:16 +0200 Subject: [PATCH 43/76] [HttpKernel] do file_exists() check instead of silent notice --- 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 f54d3589f7dfb..b5e5a2b5ca068 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -587,7 +587,7 @@ protected function initializeContainer() $errorLevel = error_reporting(\E_ALL ^ \E_WARNING); $fresh = $oldContainer = false; try { - if (\is_object($this->container = include $cache->getPath())) { + if (file_exists($cache->getPath()) && \is_object($this->container = include $cache->getPath())) { $this->container->set('kernel', $this); $oldContainer = $this->container; $fresh = true; @@ -650,7 +650,7 @@ protected function initializeContainer() } } - if (null === $oldContainer) { + if (null === $oldContainer && file_exists($cache->getPath())) { $errorLevel = error_reporting(\E_ALL ^ \E_WARNING); try { $oldContainer = include $cache->getPath(); From 9d015c7c50cac8e954276c21025c030250174013 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 10 May 2018 19:25:00 -0700 Subject: [PATCH 44/76] [Filesystem] Fix usages of error_get_last() --- .../Component/Console/Command/Command.php | 5 +- .../Component/Filesystem/Filesystem.php | 64 ++++++++++++------- src/Symfony/Component/Finder/SplFileInfo.php | 7 +- .../Component/HttpFoundation/File/File.php | 8 ++- .../HttpFoundation/File/UploadedFile.php | 8 ++- .../Component/Process/Pipes/AbstractPipes.php | 14 +++- .../Component/Process/Pipes/UnixPipes.php | 5 +- 7 files changed, 73 insertions(+), 38 deletions(-) diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php index 4b8eaea64e666..3fe12e867a827 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php @@ -205,12 +205,11 @@ public function run(InputInterface $input, OutputInterface $output) if (null !== $this->processTitle) { if (function_exists('cli_set_process_title')) { - if (false === @cli_set_process_title($this->processTitle)) { + if (!@cli_set_process_title($this->processTitle)) { if ('Darwin' === PHP_OS) { $output->writeln('Running "cli_get_process_title" as an unprivileged user is not supported on MacOS.'); } else { - $error = error_get_last(); - trigger_error($error['message'], E_USER_WARNING); + cli_set_process_title($this->processTitle); } } } elseif (function_exists('setproctitle')) { diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php index 5c3b2a2d9cb8e..0f6d0a269e6cb 100644 --- a/src/Symfony/Component/Filesystem/Filesystem.php +++ b/src/Symfony/Component/Filesystem/Filesystem.php @@ -21,6 +21,8 @@ */ class Filesystem { + private static $lastError; + /** * Copies a file. * @@ -95,12 +97,11 @@ public function mkdir($dirs, $mode = 0777) continue; } - if (true !== @mkdir($dir, $mode, true)) { - $error = error_get_last(); + if (!self::box('mkdir', $dir, $mode, true)) { if (!is_dir($dir)) { // The directory was not created by a concurrent process. Let's throw an exception with a developer friendly error message if we have one - if ($error) { - throw new IOException(sprintf('Failed to create "%s": %s.', $dir, $error['message']), 0, null, $dir); + if (self::$lastError) { + throw new IOException(sprintf('Failed to create "%s": %s.', $dir, self::$lastError), 0, null, $dir); } throw new IOException(sprintf('Failed to create "%s"', $dir), 0, null, $dir); } @@ -169,20 +170,17 @@ public function remove($files) foreach ($files as $file) { if (is_link($file)) { // See https://bugs.php.net/52176 - if (!@(unlink($file) || '\\' !== DIRECTORY_SEPARATOR || rmdir($file)) && file_exists($file)) { - $error = error_get_last(); - throw new IOException(sprintf('Failed to remove symlink "%s": %s.', $file, $error['message'])); + if (!(self::box('unlink', $file) || '\\' !== DIRECTORY_SEPARATOR || self::box('rmdir', $file)) && file_exists($file)) { + throw new IOException(sprintf('Failed to remove symlink "%s": %s.', $file, self::$lastError)); } } elseif (is_dir($file)) { $this->remove(new \FilesystemIterator($file, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS)); - if (!@rmdir($file) && file_exists($file)) { - $error = error_get_last(); - throw new IOException(sprintf('Failed to remove directory "%s": %s.', $file, $error['message'])); + if (!self::box('rmdir', $file) && file_exists($file)) { + throw new IOException(sprintf('Failed to remove directory "%s": %s.', $file, self::$lastError)); } - } elseif (!@unlink($file) && file_exists($file)) { - $error = error_get_last(); - throw new IOException(sprintf('Failed to remove file "%s": %s.', $file, $error['message'])); + } elseif (!self::box('unlink', $file) && file_exists($file)) { + throw new IOException(sprintf('Failed to remove file "%s": %s.', $file, self::$lastError)); } } } @@ -336,19 +334,16 @@ public function symlink($originDir, $targetDir, $copyOnWindows = false) $this->mkdir(dirname($targetDir)); - $ok = false; if (is_link($targetDir)) { - if (readlink($targetDir) != $originDir) { - $this->remove($targetDir); - } else { - $ok = true; + if (readlink($targetDir) === $originDir) { + return; } + $this->remove($targetDir); } - if (!$ok && true !== @symlink($originDir, $targetDir)) { - $report = error_get_last(); - if (is_array($report)) { - if ('\\' === DIRECTORY_SEPARATOR && false !== strpos($report['message'], 'error code(1314)')) { + if (!self::box('symlink', $originDir, $targetDir)) { + if (null !== self::$lastError) { + if ('\\' === DIRECTORY_SEPARATOR && false !== strpos(self::$lastError, 'error code(1314)')) { throw new IOException('Unable to create symlink due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?', 0, null, $targetDir); } } @@ -580,4 +575,29 @@ private function toIterator($files) return $files; } + + private static function box($func) + { + self::$lastError = null; + \set_error_handler(__CLASS__.'::handleError'); + try { + $result = \call_user_func_array($func, \array_slice(\func_get_args(), 1)); + \restore_error_handler(); + + return $result; + } catch (\Throwable $e) { + } catch (\Exception $e) { + } + \restore_error_handler(); + + throw $e; + } + + /** + * @internal + */ + public static function handleError($type, $msg) + { + self::$lastError = $msg; + } } diff --git a/src/Symfony/Component/Finder/SplFileInfo.php b/src/Symfony/Component/Finder/SplFileInfo.php index 19f95e26be69a..0f4e025b22bd2 100644 --- a/src/Symfony/Component/Finder/SplFileInfo.php +++ b/src/Symfony/Component/Finder/SplFileInfo.php @@ -66,12 +66,11 @@ public function getRelativePathname() */ public function getContents() { - $level = error_reporting(0); + set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; }); $content = file_get_contents($this->getPathname()); - error_reporting($level); + restore_error_handler(); if (false === $content) { - $error = error_get_last(); - throw new \RuntimeException($error['message']); + throw new \RuntimeException($error); } return $content; diff --git a/src/Symfony/Component/HttpFoundation/File/File.php b/src/Symfony/Component/HttpFoundation/File/File.php index e2a67684fcda6..65ece98379019 100644 --- a/src/Symfony/Component/HttpFoundation/File/File.php +++ b/src/Symfony/Component/HttpFoundation/File/File.php @@ -93,9 +93,11 @@ public function move($directory, $name = null) { $target = $this->getTargetFile($directory, $name); - if (!@rename($this->getPathname(), $target)) { - $error = error_get_last(); - throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error['message']))); + set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; }); + $renamed = rename($this->getPathname(), $target); + restore_error_handler(); + if (!$renamed) { + throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error))); } @chmod($target, 0666 & ~umask()); diff --git a/src/Symfony/Component/HttpFoundation/File/UploadedFile.php b/src/Symfony/Component/HttpFoundation/File/UploadedFile.php index 082d8d534e17a..39b29775ccd8e 100644 --- a/src/Symfony/Component/HttpFoundation/File/UploadedFile.php +++ b/src/Symfony/Component/HttpFoundation/File/UploadedFile.php @@ -192,9 +192,11 @@ public function move($directory, $name = null) $target = $this->getTargetFile($directory, $name); - if (!@move_uploaded_file($this->getPathname(), $target)) { - $error = error_get_last(); - throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error['message']))); + set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; }); + $moved = move_uploaded_file($this->getPathname(), $target); + restore_error_handler(); + if (!$moved) { + throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error))); } @chmod($target, 0666 & ~umask()); diff --git a/src/Symfony/Component/Process/Pipes/AbstractPipes.php b/src/Symfony/Component/Process/Pipes/AbstractPipes.php index 9a23d93c98688..97fe728bfd70d 100644 --- a/src/Symfony/Component/Process/Pipes/AbstractPipes.php +++ b/src/Symfony/Component/Process/Pipes/AbstractPipes.php @@ -23,6 +23,7 @@ abstract class AbstractPipes implements PipesInterface private $inputBuffer = ''; private $input; private $blocked = true; + private $lastError; /** * @param resource|null $input @@ -56,10 +57,11 @@ public function close() */ protected function hasSystemCallBeenInterrupted() { - $lastError = error_get_last(); + $lastError = $this->lastError; + $this->lastError = null; // stream_select returns false when the `select` system call is interrupted by an incoming signal - return isset($lastError['message']) && false !== stripos($lastError['message'], 'interrupted system call'); + return null !== $lastError && false !== stripos($lastError, 'interrupted system call'); } /** @@ -137,4 +139,12 @@ protected function write() return array($this->pipes[0]); } } + + /** + * @internal + */ + public function handleError($type, $msg) + { + $this->lastError = $msg; + } } diff --git a/src/Symfony/Component/Process/Pipes/UnixPipes.php b/src/Symfony/Component/Process/Pipes/UnixPipes.php index 65f32ecf2735a..935c43209d9da 100644 --- a/src/Symfony/Component/Process/Pipes/UnixPipes.php +++ b/src/Symfony/Component/Process/Pipes/UnixPipes.php @@ -99,7 +99,9 @@ public function readAndWrite($blocking, $close = false) unset($r[0]); // let's have a look if something changed in streams - if (($r || $w) && false === @stream_select($r, $w, $e, 0, $blocking ? Process::TIMEOUT_PRECISION * 1E6 : 0)) { + set_error_handler(array($this, 'handleError')); + if (($r || $w) && false === stream_select($r, $w, $e, 0, $blocking ? Process::TIMEOUT_PRECISION * 1E6 : 0)) { + restore_error_handler(); // if a system call has been interrupted, forget about it, let's try again // otherwise, an error occurred, let's reset pipes if (!$this->hasSystemCallBeenInterrupted()) { @@ -108,6 +110,7 @@ public function readAndWrite($blocking, $close = false) return $read; } + restore_error_handler(); foreach ($r as $pipe) { // prior PHP 5.4 the array passed to stream_select is modified and From 585ae7c6466eedf27c6e889e58996a35823eedfe Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Sun, 13 May 2018 19:11:39 +0200 Subject: [PATCH 45/76] [HttpKernel] Make TraceableValueResolver $stopwatch mandatory --- .../TraceableValueResolver.php | 4 ++-- .../ControllerArgumentValueResolverPass.php | 9 ++++---- ...ontrollerArgumentValueResolverPassTest.php | 22 ++++++++++++++++++- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/TraceableValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/TraceableValueResolver.php index 9837a057a6ae0..4d60aa15f7adb 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/TraceableValueResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/TraceableValueResolver.php @@ -26,10 +26,10 @@ final class TraceableValueResolver implements ArgumentValueResolverInterface private $inner; private $stopwatch; - public function __construct(ArgumentValueResolverInterface $inner, ?Stopwatch $stopwatch = null) + public function __construct(ArgumentValueResolverInterface $inner, Stopwatch $stopwatch) { $this->inner = $inner; - $this->stopwatch = $stopwatch ?? new Stopwatch(); + $this->stopwatch = $stopwatch; } /** diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/ControllerArgumentValueResolverPass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/ControllerArgumentValueResolverPass.php index 1b12a581f3ba3..77c0e479ae0a2 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/ControllerArgumentValueResolverPass.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/ControllerArgumentValueResolverPass.php @@ -15,7 +15,6 @@ use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpKernel\Controller\ArgumentResolver\TraceableValueResolver; use Symfony\Component\Stopwatch\Stopwatch; @@ -31,11 +30,13 @@ class ControllerArgumentValueResolverPass implements CompilerPassInterface private $argumentResolverService; private $argumentValueResolverTag; + private $traceableResolverStopwatch; - public function __construct(string $argumentResolverService = 'argument_resolver', string $argumentValueResolverTag = 'controller.argument_value_resolver') + public function __construct(string $argumentResolverService = 'argument_resolver', string $argumentValueResolverTag = 'controller.argument_value_resolver', string $traceableResolverStopwatch = 'debug.stopwatch') { $this->argumentResolverService = $argumentResolverService; $this->argumentValueResolverTag = $argumentValueResolverTag; + $this->traceableResolverStopwatch = $traceableResolverStopwatch; } public function process(ContainerBuilder $container) @@ -46,12 +47,12 @@ public function process(ContainerBuilder $container) $resolvers = $this->findAndSortTaggedServices($this->argumentValueResolverTag, $container); - if ($container->getParameter('kernel.debug') && class_exists(Stopwatch::class)) { + if ($container->getParameter('kernel.debug') && class_exists(Stopwatch::class) && $container->has($this->traceableResolverStopwatch)) { foreach ($resolvers as $resolverReference) { $id = (string) $resolverReference; $container->register("debug.$id", TraceableValueResolver::class) ->setDecoratedService($id) - ->setArguments(array(new Reference("debug.$id.inner"), new Reference('debug.stopwatch', ContainerInterface::NULL_ON_INVALID_REFERENCE))); + ->setArguments(array(new Reference("debug.$id.inner"), new Reference($this->traceableResolverStopwatch))); } } diff --git a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ControllerArgumentValueResolverPassTest.php b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ControllerArgumentValueResolverPassTest.php index 3cbc62131fa24..49bbd0f9c1ccd 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ControllerArgumentValueResolverPassTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ControllerArgumentValueResolverPassTest.php @@ -17,6 +17,7 @@ use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpKernel\Controller\ArgumentResolver; use Symfony\Component\HttpKernel\DependencyInjection\ControllerArgumentValueResolverPass; +use Symfony\Component\Stopwatch\Stopwatch; class ControllerArgumentValueResolverPassTest extends TestCase { @@ -52,7 +53,7 @@ public function testServicesAreOrderedAccordingToPriority() $this->assertFalse($container->hasDefinition('n3.traceable')); } - public function testInDebug() + public function testInDebugWithStopWatchDefinition() { $services = array( 'n3' => array(array()), @@ -68,6 +69,7 @@ public function testInDebug() $definition = new Definition(ArgumentResolver::class, array(null, array())); $container = new ContainerBuilder(); + $container->register('debug.stopwatch', Stopwatch::class); $container->setDefinition('argument_resolver', $definition); foreach ($services as $id => list($tag)) { @@ -88,6 +90,24 @@ public function testInDebug() $this->assertTrue($container->hasDefinition('n3')); } + public function testInDebugWithouStopWatchDefinition() + { + $expected = array(new Reference('n1')); + + $definition = new Definition(ArgumentResolver::class, array(null, array())); + $container = new ContainerBuilder(); + $container->register('n1')->addTag('controller.argument_value_resolver'); + $container->setDefinition('argument_resolver', $definition); + + $container->setParameter('kernel.debug', true); + + (new ControllerArgumentValueResolverPass())->process($container); + $this->assertEquals($expected, $definition->getArgument(1)->getValues()); + + $this->assertFalse($container->hasDefinition('debug.n1')); + $this->assertTrue($container->hasDefinition('n1')); + } + public function testReturningEmptyArrayWhenNoService() { $definition = new Definition(ArgumentResolver::class, array(null, array())); From 16ebb43bd4ff29b53cb78b9cd1d1f7d97de3cb32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Ostroluck=C3=BD?= Date: Sat, 12 May 2018 21:17:30 +0200 Subject: [PATCH 46/76] Disallow illegal characters like "." in session.name PHP saves cookie with correct name, but upon deserialization to $_COOKIE, it replaces some characters, e.g. "." becomes "_". This is probably also reason why \SessionHandler is not able to find a session. https://harrybailey.com/2009/04/dots-arent-allowed-in-php-cookie-names/ https://bugs.php.net/bug.php?id=75883 --- .../DependencyInjection/Configuration.php | 11 ++++- .../DependencyInjection/ConfigurationTest.php | 49 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index b21b3ee8df769..a29d8fada010a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -339,7 +339,16 @@ private function addSessionSection(ArrayNodeDefinition $rootNode) ->children() ->scalarNode('storage_id')->defaultValue('session.storage.native')->end() ->scalarNode('handler_id')->defaultValue('session.handler.native_file')->end() - ->scalarNode('name')->end() + ->scalarNode('name') + ->validate() + ->ifTrue(function ($v) { + parse_str($v, $parsed); + + return implode('&', array_keys($parsed)) !== (string) $v; + }) + ->thenInvalid('Session name %s contains illegal character(s)') + ->end() + ->end() ->scalarNode('cookie_lifetime')->end() ->scalarNode('cookie_path')->end() ->scalarNode('cookie_domain')->end() diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index a20a120d0710b..6505d5a034932 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -41,6 +41,55 @@ public function testDoNoDuplicateDefaultFormResources() $this->assertEquals(array('FrameworkBundle:Form'), $config['templating']['form']['resources']); } + /** + * @dataProvider getTestValidSessionName + */ + public function testValidSessionName($sessionName) + { + $processor = new Processor(); + $config = $processor->processConfiguration( + new Configuration(true), + array(array('session' => array('name' => $sessionName))) + ); + + $this->assertEquals($sessionName, $config['session']['name']); + } + + public function getTestValidSessionName() + { + return array( + array(null), + array('PHPSESSID'), + array('a&b'), + array(',_-!@#$%^*(){}:<>/?'), + ); + } + + /** + * @dataProvider getTestInvalidSessionName + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + */ + public function testInvalidSessionName($sessionName) + { + $processor = new Processor(); + $processor->processConfiguration( + new Configuration(true), + array(array('session' => array('name' => $sessionName))) + ); + } + + public function getTestInvalidSessionName() + { + return array( + array('a.b'), + array('a['), + array('a[]'), + array('a[b]'), + array('a=b'), + array('a+b'), + ); + } + /** * @dataProvider getTestValidTrustedProxiesData */ From d52f491bfa94d2092f9beb1e8137fc6b0f32ceab Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Mon, 14 May 2018 18:07:30 +0200 Subject: [PATCH 47/76] [Profiler] Remove propel & event_listener_loading category identifiers --- .../WebProfilerBundle/Resources/views/Collector/time.html.twig | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig index 3361be296e26b..469aee8f5fe60 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig @@ -7,10 +7,8 @@ 'default': '#aacd4e', 'section': '#666', 'event_listener': '#3dd', - 'event_listener_loading': '#add', 'template': '#dd3', 'doctrine': '#d3d', - 'propel': '#f4d', 'child_sections': '#eed', } %} {% endif %} From 6f8b8625f45f04b30bb69e227c4a2b4bc7ede528 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 13 May 2018 23:24:43 +0200 Subject: [PATCH 48/76] [DI] Shared services should not be inlined in non-shared ones --- .../Compiler/InlineServiceDefinitionsPass.php | 2 +- .../Tests/Compiler/InlineServiceDefinitionsPassTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php index c64348ed1de3b..a79e78b6b2a46 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php @@ -138,6 +138,6 @@ private function isInlineableDefinition($id, Definition $definition, ServiceRefe return false; } - return true; + return $this->container->getDefinition($ids[0])->isShared(); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php index adaa4044f453c..4abe6143152c7 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php @@ -92,7 +92,7 @@ public function testProcessDoesInlineNonSharedService() $this->assertNotSame($container->getDefinition('bar'), $arguments[2]); } - public function testProcessInlinesMixedServicesLoop() + public function testProcessDoesNotInlineMixedServicesLoop() { $container = new ContainerBuilder(); $container @@ -108,7 +108,7 @@ public function testProcessInlinesMixedServicesLoop() $this->process($container); - $this->assertEquals($container->getDefinition('foo')->getArgument(0), $container->getDefinition('bar')); + $this->assertEquals(new Reference('bar'), $container->getDefinition('foo')->getArgument(0)); } /** From b2a97ea00f8993b2efbc491ad60af1672f62ed9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Ostroluck=C3=BD?= Date: Sat, 12 May 2018 16:29:41 +0200 Subject: [PATCH 49/76] [Config] Fix tests when path contains UTF chars --- src/Symfony/Component/Config/Tests/Util/XmlUtilsTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Config/Tests/Util/XmlUtilsTest.php b/src/Symfony/Component/Config/Tests/Util/XmlUtilsTest.php index 9d61c9cd83284..10b4a7a9b4dc7 100644 --- a/src/Symfony/Component/Config/Tests/Util/XmlUtilsTest.php +++ b/src/Symfony/Component/Config/Tests/Util/XmlUtilsTest.php @@ -55,7 +55,7 @@ public function testLoadFile() XmlUtils::loadFile($fixtures.'valid.xml', array($mock, 'validate')); $this->fail(); } catch (\InvalidArgumentException $e) { - $this->assertRegExp('/The XML file "[\w:\/\\\.~+-]+" is not valid\./', $e->getMessage()); + $this->assertRegExp('/The XML file ".+" is not valid\./', $e->getMessage()); } $this->assertInstanceOf('DOMDocument', XmlUtils::loadFile($fixtures.'valid.xml', array($mock, 'validate'))); From 2400e719621fdc9b3f7b9254ed1f6d38bf35a84c Mon Sep 17 00:00:00 2001 From: Adam Szaraniec Date: Mon, 14 May 2018 21:15:07 +0400 Subject: [PATCH 50/76] use strict compare in url validator --- src/Symfony/Component/Validator/Constraints/UrlValidator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Validator/Constraints/UrlValidator.php b/src/Symfony/Component/Validator/Constraints/UrlValidator.php index 222597ed10f41..292e117d9f12d 100644 --- a/src/Symfony/Component/Validator/Constraints/UrlValidator.php +++ b/src/Symfony/Component/Validator/Constraints/UrlValidator.php @@ -92,7 +92,7 @@ public function validate($value, Constraint $constraint) Url::CHECK_DNS_TYPE_SOA, Url::CHECK_DNS_TYPE_SRV, Url::CHECK_DNS_TYPE_TXT, - ))) { + ), true)) { throw new InvalidOptionsException(sprintf('Invalid value for option "checkDNS" in constraint %s', get_class($constraint)), array('checkDNS')); } From 9cda96b8b539e85f5be3afed406905f341f4741b Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Mon, 14 May 2018 20:26:58 +0300 Subject: [PATCH 51/76] #27250 limiting GET_LOCK key up to 64 char due to changes in MySQL 5.7.5 and later --- .../Session/Storage/Handler/PdoSessionHandler.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php index dfd66516062c3..0825ee6ea9899 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php @@ -552,14 +552,16 @@ private function doAdvisoryLock($sessionId) { switch ($this->driver) { case 'mysql': + // MySQL 5.7.5 and later enforces a maximum length on lock names of 64 characters. Previously, no limit was enforced. + $lockId = \substr($sessionId, 0, 64); // should we handle the return value? 0 on timeout, null on error // we use a timeout of 50 seconds which is also the default for innodb_lock_wait_timeout $stmt = $this->pdo->prepare('SELECT GET_LOCK(:key, 50)'); - $stmt->bindValue(':key', $sessionId, \PDO::PARAM_STR); + $stmt->bindValue(':key', $lockId, \PDO::PARAM_STR); $stmt->execute(); $releaseStmt = $this->pdo->prepare('DO RELEASE_LOCK(:key)'); - $releaseStmt->bindValue(':key', $sessionId, \PDO::PARAM_STR); + $releaseStmt->bindValue(':key', $lockId, \PDO::PARAM_STR); return $releaseStmt; case 'pgsql': From f5ef421474aa291411e08e57b2d955e730580c5c Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Wed, 2 May 2018 20:53:36 +0200 Subject: [PATCH 52/76] [Messenger] Middleware factories support in config --- .../DoctrineTransactionMiddlewareFactory.php | 37 +++++++++++++ .../DependencyInjection/Configuration.php | 31 ++++++++++- .../FrameworkExtension.php | 11 ++-- .../Resources/config/schema/symfony-1.0.xsd | 9 +++- ...er_middleware_factory_erroneous_format.php | 16 ++++++ .../Fixtures/php/messenger_multiple_buses.php | 1 + .../Fixtures/xml/messenger_multiple_buses.xml | 15 ++++-- ...er_middleware_factory_erroneous_format.yml | 7 +++ .../Fixtures/yml/messenger_multiple_buses.yml | 1 + .../FrameworkExtensionTest.php | 29 ++++++++-- .../XmlFrameworkExtensionTest.php | 5 ++ .../DependencyInjection/MessengerPass.php | 31 +++++++---- .../DependencyInjection/MessengerPassTest.php | 53 +++++++++++++++++-- 13 files changed, 222 insertions(+), 24 deletions(-) create mode 100644 src/Symfony/Bridge/Doctrine/Messenger/DoctrineTransactionMiddlewareFactory.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_middleware_factory_erroneous_format.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_middleware_factory_erroneous_format.yml diff --git a/src/Symfony/Bridge/Doctrine/Messenger/DoctrineTransactionMiddlewareFactory.php b/src/Symfony/Bridge/Doctrine/Messenger/DoctrineTransactionMiddlewareFactory.php new file mode 100644 index 0000000000000..0db646fdc888d --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Messenger/DoctrineTransactionMiddlewareFactory.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Messenger; + +use Symfony\Bridge\Doctrine\ManagerRegistry; + +/** + * Create a Doctrine ORM transaction middleware to be used in a message bus from an entity manager name. + * + * @author Maxime Steinhausser + * + * @experimental in 4.1 + * @final + */ +class DoctrineTransactionMiddlewareFactory +{ + private $managerRegistry; + + public function __construct(ManagerRegistry $managerRegistry) + { + $this->managerRegistry = $managerRegistry; + } + + public function createMiddleware(string $managerName): DoctrineTransactionMiddleware + { + return new DoctrineTransactionMiddleware($this->managerRegistry, $managerName); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index a20fde031316e..ce65f52bef128 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -1061,7 +1061,36 @@ function ($a) { }) ->end() ->defaultValue(array()) - ->prototype('scalar')->end() + ->prototype('array') + ->beforeNormalization() + ->always() + ->then(function ($middleware): array { + if (!\is_array($middleware)) { + return array('id' => $middleware); + } + if (isset($middleware['id'])) { + return $middleware; + } + if (\count($middleware) > 1) { + throw new \InvalidArgumentException(sprintf('There is an error at path "framework.messenger" in one of the buses middleware definitions: expected a single entry for a middleware item config, with factory id as key and arguments as value. Got "%s".', json_encode($middleware))); + } + + return array( + 'id' => key($middleware), + 'arguments' => current($middleware), + ); + }) + ->end() + ->fixXmlConfig('argument') + ->children() + ->scalarNode('id')->isRequired()->cannotBeEmpty()->end() + ->arrayNode('arguments') + ->normalizeKeys(false) + ->defaultValue(array()) + ->prototype('variable') + ->end() + ->end() + ->end() ->end() ->end() ->end() diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index c08ad7b3903af..6b6d266036bb4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -1471,12 +1471,17 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder $config['default_bus'] = key($config['buses']); } - $defaultMiddleware = array('before' => array('logging'), 'after' => array('route_messages', 'call_message_handler')); + $defaultMiddleware = array( + 'before' => array(array('id' => 'logging')), + 'after' => array(array('id' => 'route_messages'), array('id' => 'call_message_handler')), + ); foreach ($config['buses'] as $busId => $bus) { $middleware = $bus['default_middleware'] ? array_merge($defaultMiddleware['before'], $bus['middleware'], $defaultMiddleware['after']) : $bus['middleware']; - if (!$validationConfig['enabled'] && \in_array('messenger.middleware.validation', $middleware, true)) { - throw new LogicException('The Validation middleware is only available when the Validator component is installed and enabled. Try running "composer require symfony/validator".'); + foreach ($middleware as $middlewareItem) { + if (!$validationConfig['enabled'] && 'messenger.middleware.validation' === $middlewareItem['id']) { + throw new LogicException('The Validation middleware is only available when the Validator component is installed and enabled. Try running "composer require symfony/validator".'); + } } $container->setParameter($busId.'.middleware', $middleware); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd index 96a13c8cb835e..c5e05b11a7ecc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd @@ -391,9 +391,16 @@ - + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_middleware_factory_erroneous_format.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_middleware_factory_erroneous_format.php new file mode 100644 index 0000000000000..d6cc86bd37d6c --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_middleware_factory_erroneous_format.php @@ -0,0 +1,16 @@ +loadFromExtension('framework', array( + 'messenger' => array( + 'buses' => array( + 'command_bus' => array( + 'middleware' => array( + array( + 'foo' => array('qux'), + 'bar' => array('baz'), + ), + ), + ), + ), + ), +)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_multiple_buses.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_multiple_buses.php index 51080f50368aa..5f3b2b23028a5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_multiple_buses.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_multiple_buses.php @@ -7,6 +7,7 @@ 'messenger.bus.commands' => null, 'messenger.bus.events' => array( 'middleware' => array( + array('with_factory' => array('foo', true, array('bar' => 'baz'))), 'allow_no_handler', ), ), diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_multiple_buses.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_multiple_buses.xml index 88b43585361a9..f63df5bbbe4b2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_multiple_buses.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_multiple_buses.xml @@ -9,12 +9,19 @@ - allow_no_handler + + foo + true + + baz + + + - route_messages - allow_no_handler - call_message_handler + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_middleware_factory_erroneous_format.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_middleware_factory_erroneous_format.yml new file mode 100644 index 0000000000000..74431414ba99c --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_middleware_factory_erroneous_format.yml @@ -0,0 +1,7 @@ +framework: + messenger: + buses: + command_bus: + middleware: + - foo: ['qux'] + bar: ['baz'] diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_multiple_buses.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_multiple_buses.yml index 83c6213348f04..e279ef3bba06d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_multiple_buses.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_multiple_buses.yml @@ -5,6 +5,7 @@ framework: messenger.bus.commands: ~ messenger.bus.events: middleware: + - with_factory: [foo, true, { bar: baz }] - "allow_no_handler" messenger.bus.queries: default_middleware: false diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index 8d94d32aa83b0..3461e8defe63a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -604,18 +604,41 @@ public function testMessengerWithMultipleBuses() $this->assertTrue($container->has('messenger.bus.commands')); $this->assertSame(array(), $container->getDefinition('messenger.bus.commands')->getArgument(0)); - $this->assertEquals(array('logging', 'route_messages', 'call_message_handler'), $container->getParameter('messenger.bus.commands.middleware')); + $this->assertEquals(array( + array('id' => 'logging'), + array('id' => 'route_messages'), + array('id' => 'call_message_handler'), + ), $container->getParameter('messenger.bus.commands.middleware')); $this->assertTrue($container->has('messenger.bus.events')); $this->assertSame(array(), $container->getDefinition('messenger.bus.events')->getArgument(0)); - $this->assertEquals(array('logging', 'allow_no_handler', 'route_messages', 'call_message_handler'), $container->getParameter('messenger.bus.events.middleware')); + $this->assertEquals(array( + array('id' => 'logging'), + array('id' => 'with_factory', 'arguments' => array('foo', true, array('bar' => 'baz'))), + array('id' => 'allow_no_handler', 'arguments' => array()), + array('id' => 'route_messages'), + array('id' => 'call_message_handler'), + ), $container->getParameter('messenger.bus.events.middleware')); $this->assertTrue($container->has('messenger.bus.queries')); $this->assertSame(array(), $container->getDefinition('messenger.bus.queries')->getArgument(0)); - $this->assertEquals(array('route_messages', 'allow_no_handler', 'call_message_handler'), $container->getParameter('messenger.bus.queries.middleware')); + $this->assertEquals(array( + array('id' => 'route_messages', 'arguments' => array()), + array('id' => 'allow_no_handler', 'arguments' => array()), + array('id' => 'call_message_handler', 'arguments' => array()), + ), $container->getParameter('messenger.bus.queries.middleware')); $this->assertTrue($container->hasAlias('message_bus')); $this->assertSame('messenger.bus.commands', (string) $container->getAlias('message_bus')); } + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage There is an error at path "framework.messenger" in one of the buses middleware definitions: expected a single entry for a middleware item config, with factory id as key and arguments as value. Got "{"foo":["qux"],"bar":["baz"]}" + */ + public function testMessengerMiddlewareFactoryErroneousFormat() + { + $this->createContainerFromFile('messenger_middleware_factory_erroneous_format'); + } + public function testTranslator() { $container = $this->createContainerFromFile('full'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php index 03401e2482942..a272ab33276b0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php @@ -27,4 +27,9 @@ public function testAssetsHelperIsRemovedWhenPhpTemplatingEngineIsEnabledAndAsse { $this->markTestSkipped('The assets key cannot be set to false using the XML configuration format.'); } + + public function testMessengerMiddlewareFactoryErroneousFormat() + { + $this->markTestSkipped('XML configuration will not allow eeroneous format.'); + } } diff --git a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php index 9789ec038cf43..1089027c28d93 100644 --- a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php +++ b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php @@ -221,24 +221,37 @@ private function registerBusToCollector(ContainerBuilder $container, string $bus $container->getDefinition('messenger.data_collector')->addMethodCall('registerBus', array($busId, new Reference($tracedBusId))); } - private function registerBusMiddleware(ContainerBuilder $container, string $busId, array $middleware) + private function registerBusMiddleware(ContainerBuilder $container, string $busId, array $middlewareCollection) { - $container->getDefinition($busId)->replaceArgument(0, array_map(function (string $name) use ($container, $busId) { - if (!$container->has($messengerMiddlewareId = 'messenger.middleware.'.$name)) { - $messengerMiddlewareId = $name; + $middlewareReferences = array(); + foreach ($middlewareCollection as $middlewareItem) { + $id = $middlewareItem['id']; + $arguments = $middlewareItem['arguments'] ?? array(); + if (!$container->has($messengerMiddlewareId = 'messenger.middleware.'.$id)) { + $messengerMiddlewareId = $id; } if (!$container->has($messengerMiddlewareId)) { - throw new RuntimeException(sprintf('Invalid middleware "%s": define such service to be able to use it.', $name)); + throw new RuntimeException(sprintf('Invalid middleware "%s": define such service to be able to use it.', $id)); } - if ($container->getDefinition($messengerMiddlewareId)->isAbstract()) { + if (($definition = $container->findDefinition($messengerMiddlewareId))->isAbstract()) { $childDefinition = new ChildDefinition($messengerMiddlewareId); + $count = \count($definition->getArguments()); + foreach (array_values($arguments ?? array()) as $key => $argument) { + // Parent definition can provide default arguments. + // Replace each explicitly or add if not set: + $key < $count ? $childDefinition->replaceArgument($key, $argument) : $childDefinition->addArgument($argument); + } - $container->setDefinition($messengerMiddlewareId = $busId.'.middleware.'.$name, $childDefinition); + $container->setDefinition($messengerMiddlewareId = $busId.'.middleware.'.$id, $childDefinition); + } elseif ($arguments) { + throw new RuntimeException(sprintf('Invalid middleware factory "%s": a middleware factory must be an abstract definition.', $id)); } - return new Reference($messengerMiddlewareId); - }, $middleware)); + $middlewareReferences[] = new Reference($messengerMiddlewareId); + } + + $container->getDefinition($busId)->replaceArgument(0, $middlewareReferences); } } diff --git a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php index eeb5d585f5cda..0bcec20fcbce3 100644 --- a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php +++ b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Compiler\ResolveChildDefinitionsPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ServiceLocator; @@ -312,14 +313,42 @@ public function testRegistersMiddlewareFromServices() $container = $this->getContainerBuilder(); $container->register($fooBusId = 'messenger.bus.foo', MessageBusInterface::class)->setArgument(0, array())->addTag('messenger.bus'); $container->register('messenger.middleware.allow_no_handler', AllowNoHandlerMiddleware::class)->setAbstract(true); + $container->register('middleware_with_factory', UselessMiddleware::class)->addArgument('some_default')->setAbstract(true); + $container->register('middleware_with_factory_using_default', UselessMiddleware::class)->addArgument('some_default')->setAbstract(true); $container->register(UselessMiddleware::class, UselessMiddleware::class); - $container->setParameter($middlewareParameter = $fooBusId.'.middleware', array(UselessMiddleware::class, 'allow_no_handler')); + $container->setParameter($middlewareParameter = $fooBusId.'.middleware', array( + array('id' => UselessMiddleware::class), + array('id' => 'middleware_with_factory', 'arguments' => array('foo', 'bar')), + array('id' => 'middleware_with_factory_using_default'), + array('id' => 'allow_no_handler'), + )); (new MessengerPass())->process($container); + (new ResolveChildDefinitionsPass())->process($container); $this->assertTrue($container->hasDefinition($childMiddlewareId = $fooBusId.'.middleware.allow_no_handler')); - $this->assertEquals(array(new Reference(UselessMiddleware::class), new Reference($childMiddlewareId)), $container->getDefinition($fooBusId)->getArgument(0)); + + $this->assertTrue($container->hasDefinition($factoryChildMiddlewareId = $fooBusId.'.middleware.middleware_with_factory')); + $this->assertEquals( + array('foo', 'bar'), + $container->getDefinition($factoryChildMiddlewareId)->getArguments(), + 'parent default argument is overridden, and next ones appended' + ); + + $this->assertTrue($container->hasDefinition($factoryWithDefaultChildMiddlewareId = $fooBusId.'.middleware.middleware_with_factory_using_default')); + $this->assertEquals( + array('some_default'), + $container->getDefinition($factoryWithDefaultChildMiddlewareId)->getArguments(), + 'parent default argument is used' + ); + + $this->assertEquals(array( + new Reference(UselessMiddleware::class), + new Reference($factoryChildMiddlewareId), + new Reference($factoryWithDefaultChildMiddlewareId), + new Reference($childMiddlewareId), + ), $container->getDefinition($fooBusId)->getArgument(0)); $this->assertFalse($container->hasParameter($middlewareParameter)); } @@ -331,7 +360,25 @@ public function testCannotRegistersAnUndefinedMiddleware() { $container = $this->getContainerBuilder(); $container->register($fooBusId = 'messenger.bus.foo', MessageBusInterface::class)->setArgument(0, array())->addTag('messenger.bus'); - $container->setParameter($middlewareParameter = $fooBusId.'.middleware', array('not_defined_middleware')); + $container->setParameter($middlewareParameter = $fooBusId.'.middleware', array( + array('id' => 'not_defined_middleware', 'arguments' => array()), + )); + + (new MessengerPass())->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Invalid middleware factory "not_an_abstract_definition": a middleware factory must be an abstract definition. + */ + public function testMiddlewareFactoryDefinitionMustBeAbstract() + { + $container = $this->getContainerBuilder(); + $container->register('not_an_abstract_definition', UselessMiddleware::class); + $container->register($fooBusId = 'messenger.bus.foo', MessageBusInterface::class)->setArgument(0, array())->addTag('messenger.bus', array('name' => 'foo')); + $container->setParameter($middlewareParameter = $fooBusId.'.middleware', array( + array('id' => 'not_an_abstract_definition', 'arguments' => array('foo')), + )); (new MessengerPass())->process($container); } From 5516b329afee4b7ed9601f39911440ad673eff80 Mon Sep 17 00:00:00 2001 From: ncou Date: Mon, 14 May 2018 10:51:37 +0200 Subject: [PATCH 53/76] Cleanup 2 tests for the HttpException classes --- .../UnprocessableEntityHttpExceptionTest.php | 15 --------------- .../UnsupportedMediaTypeHttpExceptionTest.php | 12 +----------- 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Tests/Exception/UnprocessableEntityHttpExceptionTest.php b/src/Symfony/Component/HttpKernel/Tests/Exception/UnprocessableEntityHttpExceptionTest.php index 760366c6943a1..05d8d787aa5c5 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Exception/UnprocessableEntityHttpExceptionTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Exception/UnprocessableEntityHttpExceptionTest.php @@ -6,21 +6,6 @@ class UnprocessableEntityHttpExceptionTest extends HttpExceptionTest { - /** - * Test that setting the headers using the setter function - * is working as expected. - * - * @param array $headers The headers to set - * - * @dataProvider headerDataProvider - */ - public function testHeadersSetter($headers) - { - $exception = new UnprocessableEntityHttpException(10); - $exception->setHeaders($headers); - $this->assertSame($headers, $exception->getHeaders()); - } - protected function createException() { return new UnprocessableEntityHttpException(); diff --git a/src/Symfony/Component/HttpKernel/Tests/Exception/UnsupportedMediaTypeHttpExceptionTest.php b/src/Symfony/Component/HttpKernel/Tests/Exception/UnsupportedMediaTypeHttpExceptionTest.php index d47287a1fbc69..4dae039c11fc1 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Exception/UnsupportedMediaTypeHttpExceptionTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Exception/UnsupportedMediaTypeHttpExceptionTest.php @@ -6,17 +6,7 @@ class UnsupportedMediaTypeHttpExceptionTest extends HttpExceptionTest { - /** - * @dataProvider headerDataProvider - */ - public function testHeadersSetter($headers) - { - $exception = new UnsupportedMediaTypeHttpException(10); - $exception->setHeaders($headers); - $this->assertSame($headers, $exception->getHeaders()); - } - - protected function createException($headers = array()) + protected function createException() { return new UnsupportedMediaTypeHttpException(); } From 1c3b1055dfb3b8ea5318743f480715b9d40a6635 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 15 May 2018 09:06:31 +0200 Subject: [PATCH 54/76] [DI] Allow defining bindings on ChildDefinition --- .../DependencyInjection/ChildDefinition.php | 8 ------- .../Compiler/ResolveChildDefinitionsPass.php | 2 +- .../ResolveChildDefinitionsPassTest.php | 21 +++++++++++++++++++ 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/ChildDefinition.php b/src/Symfony/Component/DependencyInjection/ChildDefinition.php index f363a64829c24..61460ca2e0f71 100644 --- a/src/Symfony/Component/DependencyInjection/ChildDefinition.php +++ b/src/Symfony/Component/DependencyInjection/ChildDefinition.php @@ -121,14 +121,6 @@ public function setInstanceofConditionals(array $instanceof) { throw new BadMethodCallException('A ChildDefinition cannot have instanceof conditionals set on it.'); } - - /** - * @internal - */ - public function setBindings(array $bindings) - { - throw new BadMethodCallException('A ChildDefinition cannot have bindings set on it.'); - } } class_alias(ChildDefinition::class, DefinitionDecorator::class); diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveChildDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveChildDefinitionsPass.php index a124e472aca81..eefb0cefd4ff7 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveChildDefinitionsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveChildDefinitionsPass.php @@ -103,7 +103,7 @@ private function doResolveDefinition(ChildDefinition $definition) $def->setAutowired($parentDef->isAutowired()); $def->setChanges($parentDef->getChanges()); - $def->setBindings($parentDef->getBindings()); + $def->setBindings($definition->getBindings() + $parentDef->getBindings()); // overwrite with values specified in the decorator $changes = $definition->getChanges(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php index 23a1915fd777c..18c0fb7c5987f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php @@ -382,6 +382,27 @@ public function testProcessSetsArguments() $this->assertSame(array(2, 1, 'foo' => 3), $def->getArguments()); } + public function testBindings() + { + $container = new ContainerBuilder(); + + $container->register('parent', 'stdClass') + ->setBindings(array('a' => '1', 'b' => '2')) + ; + + $child = $container->setDefinition('child', new ChildDefinition('parent')) + ->setBindings(array('b' => 'B', 'c' => 'C')) + ; + + $this->process($container); + + $bindings = array(); + foreach ($container->getDefinition('child')->getBindings() as $k => $v) { + $bindings[$k] = $v->getValues()[0]; + } + $this->assertEquals(array('b' => 'B', 'c' => 'C', 'a' => '1'), $bindings); + } + public function testSetAutoconfiguredOnServiceIsParent() { $container = new ContainerBuilder(); From cb5ce8f32ebb4217c291331590b8ed1cb1e5ca4f Mon Sep 17 00:00:00 2001 From: smaine Date: Tue, 15 May 2018 00:21:15 +0200 Subject: [PATCH 55/76] fix bug when imported routes are prefixed --- src/Symfony/Component/Routing/RouteCollection.php | 3 +++ .../Component/Routing/Tests/RouteCollectionTest.php | 13 +++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/Symfony/Component/Routing/RouteCollection.php b/src/Symfony/Component/Routing/RouteCollection.php index 84719e2c82fa5..4d14ca96db416 100644 --- a/src/Symfony/Component/Routing/RouteCollection.php +++ b/src/Symfony/Component/Routing/RouteCollection.php @@ -162,6 +162,9 @@ public function addNamePrefix(string $prefix) foreach ($this->routes as $name => $route) { $prefixedRoutes[$prefix.$name] = $route; + if (null !== $name = $route->getDefault('_canonical_route')) { + $route->setDefault('_canonical_route', $prefix.$name); + } } $this->routes = $prefixedRoutes; diff --git a/src/Symfony/Component/Routing/Tests/RouteCollectionTest.php b/src/Symfony/Component/Routing/Tests/RouteCollectionTest.php index 3527e12895683..0ea23f673460f 100644 --- a/src/Symfony/Component/Routing/Tests/RouteCollectionTest.php +++ b/src/Symfony/Component/Routing/Tests/RouteCollectionTest.php @@ -317,4 +317,17 @@ public function testAddNamePrefix() $this->assertNull($collection->get('foo')); $this->assertNull($collection->get('bar')); } + + public function testAddNamePrefixCanonicalRouteName() + { + $collection = new RouteCollection(); + $collection->add('foo', new Route('/foo', array('_canonical_route' => 'foo'))); + $collection->add('bar', new Route('/bar', array('_canonical_route' => 'bar'))); + $collection->add('api_foo', new Route('/api/foo', array('_canonical_route' => 'api_foo'))); + $collection->addNamePrefix('api_'); + + $this->assertEquals('api_foo', $collection->get('api_foo')->getDefault('_canonical_route')); + $this->assertEquals('api_bar', $collection->get('api_bar')->getDefault('_canonical_route')); + $this->assertEquals('api_api_foo', $collection->get('api_api_foo')->getDefault('_canonical_route')); + } } From 9e88eb5aa980e09f0f9ac691ebbe08d1baac0e0a Mon Sep 17 00:00:00 2001 From: MatTheCat Date: Fri, 11 May 2018 20:46:08 +0200 Subject: [PATCH 56/76] [Security] Fix logout --- .../DependencyInjection/SecurityExtension.php | 25 +++++++------- .../Resources/config/security.xml | 1 + .../Security/FirewallContext.php | 7 ++-- .../SecurityBundle/Security/FirewallMap.php | 2 +- .../CompleteConfigurationTest.php | 1 - .../Tests/Functional/LogoutTest.php | 34 +++++++++++++++++++ .../app/RememberMeLogout/bundles.php | 18 ++++++++++ .../app/RememberMeLogout/config.yml | 25 ++++++++++++++ .../app/RememberMeLogout/routing.yml | 5 +++ .../Bundle/SecurityBundle/composer.json | 2 +- .../Component/Security/Http/Firewall.php | 13 +++++-- .../Component/Security/Http/FirewallMap.php | 9 ++--- 12 files changed, 119 insertions(+), 23 deletions(-) create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/bundles.php create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/config.yml create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/routing.yml diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 3ffe2876e88d8..9c526f88daa9d 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -222,13 +222,14 @@ private function createFirewalls($config, ContainerBuilder $container) $mapDef = $container->getDefinition('security.firewall.map'); $map = $authenticationProviders = array(); foreach ($firewalls as $name => $firewall) { - list($matcher, $listeners, $exceptionListener) = $this->createFirewall($container, $name, $firewall, $authenticationProviders, $providerIds); + list($matcher, $listeners, $exceptionListener, $logoutListener) = $this->createFirewall($container, $name, $firewall, $authenticationProviders, $providerIds); $contextId = 'security.firewall.map.context.'.$name; $context = $container->setDefinition($contextId, new DefinitionDecorator('security.firewall.context')); $context ->replaceArgument(0, $listeners) ->replaceArgument(1, $exceptionListener) + ->replaceArgument(2, $logoutListener) ; $map[$contextId] = $matcher; } @@ -259,7 +260,7 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a // Security disabled? if (false === $firewall['security']) { - return array($matcher, array(), null); + return array($matcher, array(), null, null); } // Provider id (take the first registered provider if none defined) @@ -286,15 +287,15 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a } // Logout listener + $logoutListenerId = null; if (isset($firewall['logout'])) { - $listenerId = 'security.logout_listener.'.$id; - $listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.logout_listener')); - $listener->replaceArgument(3, array( + $logoutListenerId = 'security.logout_listener.'.$id; + $logoutListener = $container->setDefinition($logoutListenerId, new DefinitionDecorator('security.logout_listener')); + $logoutListener->replaceArgument(3, array( 'csrf_parameter' => $firewall['logout']['csrf_parameter'], 'intention' => $firewall['logout']['csrf_token_id'], 'logout_path' => $firewall['logout']['path'], )); - $listeners[] = new Reference($listenerId); // add logout success handler if (isset($firewall['logout']['success_handler'])) { @@ -304,16 +305,16 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a $logoutSuccessHandler = $container->setDefinition($logoutSuccessHandlerId, new DefinitionDecorator('security.logout.success_handler')); $logoutSuccessHandler->replaceArgument(1, $firewall['logout']['target']); } - $listener->replaceArgument(2, new Reference($logoutSuccessHandlerId)); + $logoutListener->replaceArgument(2, new Reference($logoutSuccessHandlerId)); // add CSRF provider if (isset($firewall['logout']['csrf_token_generator'])) { - $listener->addArgument(new Reference($firewall['logout']['csrf_token_generator'])); + $logoutListener->addArgument(new Reference($firewall['logout']['csrf_token_generator'])); } // add session logout handler if (true === $firewall['logout']['invalidate_session'] && false === $firewall['stateless']) { - $listener->addMethodCall('addHandler', array(new Reference('security.logout.handler.session'))); + $logoutListener->addMethodCall('addHandler', array(new Reference('security.logout.handler.session'))); } // add cookie logout handler @@ -322,12 +323,12 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a $cookieHandler = $container->setDefinition($cookieHandlerId, new DefinitionDecorator('security.logout.handler.cookie_clearing')); $cookieHandler->addArgument($firewall['logout']['delete_cookies']); - $listener->addMethodCall('addHandler', array(new Reference($cookieHandlerId))); + $logoutListener->addMethodCall('addHandler', array(new Reference($cookieHandlerId))); } // add custom handlers foreach ($firewall['logout']['handlers'] as $handlerId) { - $listener->addMethodCall('addHandler', array(new Reference($handlerId))); + $logoutListener->addMethodCall('addHandler', array(new Reference($handlerId))); } // register with LogoutUrlGenerator @@ -362,7 +363,7 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a // Exception listener $exceptionListener = new Reference($this->createExceptionListener($container, $firewall, $id, $configuredEntryPoint ?: $defaultEntryPoint, $firewall['stateless'])); - return array($matcher, $listeners, $exceptionListener); + return array($matcher, $listeners, $exceptionListener, null !== $logoutListenerId ? new Reference($logoutListenerId) : null); } private function createContextListener($container, $contextKey) diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml index b7c1407c1cc56..b044ccba98e74 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml @@ -150,6 +150,7 @@ + diff --git a/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php b/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php index 13d096d97e951..e9f8fe66d6395 100644 --- a/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php +++ b/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php @@ -12,6 +12,7 @@ namespace Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\Security\Http\Firewall\ExceptionListener; +use Symfony\Component\Security\Http\Firewall\LogoutListener; /** * This is a wrapper around the actual firewall configuration which allows us @@ -23,15 +24,17 @@ class FirewallContext { private $listeners; private $exceptionListener; + private $logoutListener; - public function __construct(array $listeners, ExceptionListener $exceptionListener = null) + public function __construct(array $listeners, ExceptionListener $exceptionListener = null, LogoutListener $logoutListener = null) { $this->listeners = $listeners; $this->exceptionListener = $exceptionListener; + $this->logoutListener = $logoutListener; } public function getContext() { - return array($this->listeners, $this->exceptionListener); + return array($this->listeners, $this->exceptionListener, $this->logoutListener); } } diff --git a/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php b/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php index dc87681c37511..d45d7b87f04a6 100644 --- a/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php +++ b/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php @@ -41,6 +41,6 @@ public function getListeners(Request $request) } } - return array(array(), null); + return array(array(), null, null); } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php index 855bed9b6899d..371baa704a835 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php @@ -76,7 +76,6 @@ public function testFirewalls() array(), array( 'security.channel_listener', - 'security.logout_listener.secure', 'security.authentication.listener.x509.secure', 'security.authentication.listener.remote_user.secure', 'security.authentication.listener.form.secure', diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php new file mode 100644 index 0000000000000..7eeb7c21171ce --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional; + +class LogoutTest extends WebTestCase +{ + public function testSessionLessRememberMeLogout() + { + $client = $this->createClient(array('test_case' => 'RememberMeLogout', 'root_config' => 'config.yml')); + + $client->request('POST', '/login', array( + '_username' => 'johannes', + '_password' => 'test', + )); + + $cookieJar = $client->getCookieJar(); + $cookieJar->expire(session_name()); + + $this->assertNotNull($cookieJar->get('REMEMBERME')); + + $client->request('GET', '/logout'); + + $this->assertNull($cookieJar->get('REMEMBERME')); + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/bundles.php new file mode 100644 index 0000000000000..d90f774abde2b --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\SecurityBundle\SecurityBundle; +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; + +return array( + new FrameworkBundle(), + new SecurityBundle(), +); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/config.yml new file mode 100644 index 0000000000000..48fd4ed6cc3cd --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/config.yml @@ -0,0 +1,25 @@ +imports: + - { resource: ./../config/framework.yml } + +security: + encoders: + Symfony\Component\Security\Core\User\User: plaintext + + providers: + in_memory: + memory: + users: + johannes: { password: test, roles: [ROLE_USER] } + + firewalls: + default: + form_login: + check_path: login + remember_me: true + require_previous_session: false + remember_me: + always_remember_me: true + key: key + logout: ~ + anonymous: ~ + stateless: true diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/routing.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/routing.yml new file mode 100644 index 0000000000000..1dddfca2f8154 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/routing.yml @@ -0,0 +1,5 @@ +login: + path: /login + +logout: + path: /logout diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index e765b16bef867..90edcf6e38cee 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -18,7 +18,7 @@ "require": { "php": ">=5.3.9", "ext-xml": "*", - "symfony/security": "~2.7.38|~2.8.31", + "symfony/security": "~2.7.47|~2.8.40", "symfony/security-acl": "~2.7", "symfony/http-kernel": "~2.7" }, diff --git a/src/Symfony/Component/Security/Http/Firewall.php b/src/Symfony/Component/Security/Http/Firewall.php index 62b0071212e54..376194d3a6c8c 100644 --- a/src/Symfony/Component/Security/Http/Firewall.php +++ b/src/Symfony/Component/Security/Http/Firewall.php @@ -47,20 +47,29 @@ public function onKernelRequest(GetResponseEvent $event) } // register listeners for this firewall - list($listeners, $exceptionListener) = $this->map->getListeners($event->getRequest()); + $listeners = $this->map->getListeners($event->getRequest()); + + $authenticationListeners = $listeners[0]; + $exceptionListener = $listeners[1]; + $logoutListener = isset($listeners[2]) ? $listeners[2] : null; + if (null !== $exceptionListener) { $this->exceptionListeners[$event->getRequest()] = $exceptionListener; $exceptionListener->register($this->dispatcher); } // initiate the listener chain - foreach ($listeners as $listener) { + foreach ($authenticationListeners as $listener) { $listener->handle($event); if ($event->hasResponse()) { break; } } + + if (null !== $logoutListener) { + $logoutListener->handle($event); + } } public function onKernelFinishRequest(FinishRequestEvent $event) diff --git a/src/Symfony/Component/Security/Http/FirewallMap.php b/src/Symfony/Component/Security/Http/FirewallMap.php index e767d123cb03e..fc97410d4e698 100644 --- a/src/Symfony/Component/Security/Http/FirewallMap.php +++ b/src/Symfony/Component/Security/Http/FirewallMap.php @@ -14,6 +14,7 @@ use Symfony\Component\HttpFoundation\RequestMatcherInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Http\Firewall\ExceptionListener; +use Symfony\Component\Security\Http\Firewall\LogoutListener; /** * FirewallMap allows configuration of different firewalls for specific parts @@ -25,9 +26,9 @@ class FirewallMap implements FirewallMapInterface { private $map = array(); - public function add(RequestMatcherInterface $requestMatcher = null, array $listeners = array(), ExceptionListener $exceptionListener = null) + public function add(RequestMatcherInterface $requestMatcher = null, array $listeners = array(), ExceptionListener $exceptionListener = null, LogoutListener $logoutListener = null) { - $this->map[] = array($requestMatcher, $listeners, $exceptionListener); + $this->map[] = array($requestMatcher, $listeners, $exceptionListener, $logoutListener); } /** @@ -37,10 +38,10 @@ public function getListeners(Request $request) { foreach ($this->map as $elements) { if (null === $elements[0] || $elements[0]->matches($request)) { - return array($elements[1], $elements[2]); + return array($elements[1], $elements[2], $elements[3]); } } - return array(array(), null); + return array(array(), null, null); } } From 97cbea005e6e1e5c238a8e1d6a753a54404811a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Ostroluck=C3=BD?= Date: Tue, 15 May 2018 20:17:27 +0200 Subject: [PATCH 57/76] [Lock] Skip test if posix extension is not installed This isn't installed by default on Fedora --- .../Component/Lock/Tests/Store/BlockingStoreTestTrait.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Symfony/Component/Lock/Tests/Store/BlockingStoreTestTrait.php b/src/Symfony/Component/Lock/Tests/Store/BlockingStoreTestTrait.php index 93e5a54793f31..588f0028a4944 100644 --- a/src/Symfony/Component/Lock/Tests/Store/BlockingStoreTestTrait.php +++ b/src/Symfony/Component/Lock/Tests/Store/BlockingStoreTestTrait.php @@ -31,6 +31,7 @@ abstract protected function getStore(); * This test is time sensible: the $clockDelay could be adjust. * * @requires extension pcntl + * @requires extension posix * @requires function pcntl_sigwaitinfo */ public function testBlockingLocks() From f49310b54cd3e44779819717617989843bb77325 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 16 May 2018 10:49:48 +0200 Subject: [PATCH 58/76] fix merge --- .../Tests/Functional/app/RememberMeLogout/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/config.yml index 48fd4ed6cc3cd..60e9cb89a229a 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/config.yml @@ -19,7 +19,7 @@ security: require_previous_session: false remember_me: always_remember_me: true - key: key + secret: key logout: ~ anonymous: ~ stateless: true From ba5cb1a24548b86835be960867687c1f3d3bec05 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 11 May 2018 17:58:37 +0200 Subject: [PATCH 59/76] fixed CS --- .../PhpUnit/Legacy/CoverageListenerTrait.php | 1 - .../weak_vendors_on_eval_d_deprecation.phpt | 2 +- src/Symfony/Bridge/Twig/NodeVisitor/Scope.php | 2 +- .../Tests/Controller/ControllerTest.php | 1 - .../DataCollector/CacheDataCollector.php | 18 ++--- .../CssSelector/Node/Specificity.php | 4 +- .../Component/CssSelector/XPath/XPathExpr.php | 2 - .../Debug/Exception/FlattenException.php | 2 +- .../Configurator/InstanceofConfigurator.php | 2 +- src/Symfony/Component/Form/FormView.php | 2 +- .../Tests/AbstractBootstrap4LayoutTest.php | 81 ++++++++++--------- .../ArgumentMetadataFactory.php | 28 +++++-- .../HttpKernel/Profiler/Profiler.php | 2 +- .../ArgumentMetadataFactoryTest.php | 4 +- .../Tests/Profiler/ProfilerTest.php | 2 +- .../Tests/Fixtures/ParentDummy.php | 2 +- src/Symfony/Component/PropertyInfo/Type.php | 4 +- .../Matcher/Dumper/DumperCollection.php | 2 +- .../Routing/RouteCollectionBuilder.php | 4 +- .../Http/Firewall/ContextListener.php | 8 +- .../Mapping/AttributeMetadataInterface.php | 2 +- .../Mapping/ClassMetadataInterface.php | 2 +- .../Normalizer/AbstractObjectNormalizer.php | 1 - .../Translation/MessageCatalogueInterface.php | 4 +- .../Validator/Mapping/ClassMetadata.php | 2 +- 25 files changed, 96 insertions(+), 88 deletions(-) diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerTrait.php b/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerTrait.php index 1c84ff32c0b74..ab1cda6702be5 100644 --- a/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerTrait.php +++ b/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerTrait.php @@ -11,7 +11,6 @@ namespace Symfony\Bridge\PhpUnit\Legacy; -use PHPUnit\Framework\Test; use PHPUnit\Framework\TestCase; use PHPUnit\Framework\Warning; diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_eval_d_deprecation.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_eval_d_deprecation.phpt index 8fa436e20178b..8390d16332fa1 100644 --- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_eval_d_deprecation.phpt +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_eval_d_deprecation.phpt @@ -15,7 +15,7 @@ while (!file_exists($vendor.'/vendor')) { define('PHPUNIT_COMPOSER_INSTALL', $vendor.'/vendor/autoload.php'); require PHPUNIT_COMPOSER_INSTALL; require_once __DIR__.'/../../bootstrap.php'; -eval("@trigger_error('who knows where I come from?', E_USER_DEPRECATED);") +eval("@trigger_error('who knows where I come from?', E_USER_DEPRECATED);"); ?> --EXPECTF-- diff --git a/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php b/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php index 1c3451bbebf46..59497dc961984 100644 --- a/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php +++ b/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php @@ -20,7 +20,7 @@ class Scope private $data = array(); private $left = false; - public function __construct(Scope $parent = null) + public function __construct(self $parent = null) { $this->parent = $parent; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTest.php index 9fd20649289de..452845cea8e34 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTest.php @@ -12,7 +12,6 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; -use Symfony\Component\HttpFoundation\File\File; class ControllerTest extends ControllerTraitTest { diff --git a/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php b/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php index 962fb25cb94cb..ac5c36d2fd83a 100644 --- a/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php +++ b/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php @@ -122,30 +122,30 @@ private function calculateStatistics() ); /** @var TraceableAdapterEvent $call */ foreach ($calls as $call) { - $statistics[$name]['calls'] += 1; + ++$statistics[$name]['calls']; $statistics[$name]['time'] += $call->end - $call->start; if ('getItem' === $call->name) { - $statistics[$name]['reads'] += 1; + ++$statistics[$name]['reads']; if ($call->hits) { - $statistics[$name]['hits'] += 1; + ++$statistics[$name]['hits']; } else { - $statistics[$name]['misses'] += 1; + ++$statistics[$name]['misses']; } } elseif ('getItems' === $call->name) { $statistics[$name]['reads'] += $call->hits + $call->misses; $statistics[$name]['hits'] += $call->hits; $statistics[$name]['misses'] += $call->misses; } elseif ('hasItem' === $call->name) { - $statistics[$name]['reads'] += 1; + ++$statistics[$name]['reads']; if (false === $call->result) { - $statistics[$name]['misses'] += 1; + ++$statistics[$name]['misses']; } else { - $statistics[$name]['hits'] += 1; + ++$statistics[$name]['hits']; } } elseif ('save' === $call->name) { - $statistics[$name]['writes'] += 1; + ++$statistics[$name]['writes']; } elseif ('deleteItem' === $call->name) { - $statistics[$name]['deletes'] += 1; + ++$statistics[$name]['deletes']; } } if ($statistics[$name]['reads']) { diff --git a/src/Symfony/Component/CssSelector/Node/Specificity.php b/src/Symfony/Component/CssSelector/Node/Specificity.php index 6aa70d781cae2..a11b7f73d89c8 100644 --- a/src/Symfony/Component/CssSelector/Node/Specificity.php +++ b/src/Symfony/Component/CssSelector/Node/Specificity.php @@ -48,7 +48,7 @@ public function __construct($a, $b, $c) /** * @return self */ - public function plus(Specificity $specificity) + public function plus(self $specificity) { return new self($this->a + $specificity->a, $this->b + $specificity->b, $this->c + $specificity->c); } @@ -69,7 +69,7 @@ public function getValue() * * @return int */ - public function compareTo(Specificity $specificity) + public function compareTo(self $specificity) { if ($this->a !== $specificity->a) { return $this->a > $specificity->a ? 1 : -1; diff --git a/src/Symfony/Component/CssSelector/XPath/XPathExpr.php b/src/Symfony/Component/CssSelector/XPath/XPathExpr.php index 63e3ac36bf820..a1e244c9e45d8 100644 --- a/src/Symfony/Component/CssSelector/XPath/XPathExpr.php +++ b/src/Symfony/Component/CssSelector/XPath/XPathExpr.php @@ -53,8 +53,6 @@ public function getElement() } /** - * @param $condition - * * @return $this */ public function addCondition($condition) diff --git a/src/Symfony/Component/Debug/Exception/FlattenException.php b/src/Symfony/Component/Debug/Exception/FlattenException.php index 24679dcaab242..f491bf2ac4c92 100644 --- a/src/Symfony/Component/Debug/Exception/FlattenException.php +++ b/src/Symfony/Component/Debug/Exception/FlattenException.php @@ -157,7 +157,7 @@ public function getPrevious() return $this->previous; } - public function setPrevious(FlattenException $previous) + public function setPrevious(self $previous) { $this->previous = $previous; } diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/InstanceofConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/InstanceofConfigurator.php index 1d3975bf8794e..629874d19cd14 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/InstanceofConfigurator.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/InstanceofConfigurator.php @@ -34,7 +34,7 @@ class InstanceofConfigurator extends AbstractServiceConfigurator * * @param string $fqcn * - * @return InstanceofConfigurator + * @return self */ final protected function setInstanceof($fqcn) { diff --git a/src/Symfony/Component/Form/FormView.php b/src/Symfony/Component/Form/FormView.php index 21a3d5036ad2b..9fbca4379688a 100644 --- a/src/Symfony/Component/Form/FormView.php +++ b/src/Symfony/Component/Form/FormView.php @@ -51,7 +51,7 @@ class FormView implements \ArrayAccess, \IteratorAggregate, \Countable private $methodRendered = false; - public function __construct(FormView $parent = null) + public function __construct(self $parent = null) { $this->parent = $parent; } diff --git a/src/Symfony/Component/Form/Tests/AbstractBootstrap4LayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractBootstrap4LayoutTest.php index 2cadba0ba9e67..f9c4613e591c7 100644 --- a/src/Symfony/Component/Form/Tests/AbstractBootstrap4LayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractBootstrap4LayoutTest.php @@ -11,6 +11,15 @@ namespace Symfony\Component\Form\Tests; +use Symfony\Component\Form\Extension\Core\Type\ButtonType; +use Symfony\Component\Form\Extension\Core\Type\CheckboxType; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; +use Symfony\Component\Form\Extension\Core\Type\DateType; +use Symfony\Component\Form\Extension\Core\Type\FileType; +use Symfony\Component\Form\Extension\Core\Type\MoneyType; +use Symfony\Component\Form\Extension\Core\Type\PercentType; +use Symfony\Component\Form\Extension\Core\Type\RadioType; +use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormError; /** @@ -22,7 +31,7 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest { public function testRow() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $form = $this->factory->createNamed('name', TextType::class); $form->addError(new FormError('[trans]Error![/trans]')); $view = $form->createView(); $html = $this->renderRow($view); @@ -47,7 +56,7 @@ public function testRow() public function testLabelOnForm() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType'); + $form = $this->factory->createNamed('name', DateType::class); $view = $form->createView(); $this->renderWidget($view, array('label' => 'foo')); $html = $this->renderLabel($view); @@ -62,7 +71,7 @@ public function testLabelOnForm() public function testLabelDoesNotRenderFieldAttributes() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $form = $this->factory->createNamed('name', TextType::class); $html = $this->renderLabel($form->createView(), null, array( 'attr' => array( 'class' => 'my&class', @@ -79,7 +88,7 @@ public function testLabelDoesNotRenderFieldAttributes() public function testLabelWithCustomAttributesPassedDirectly() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $form = $this->factory->createNamed('name', TextType::class); $html = $this->renderLabel($form->createView(), null, array( 'label_attr' => array( 'class' => 'my&class', @@ -96,7 +105,7 @@ public function testLabelWithCustomAttributesPassedDirectly() public function testLabelWithCustomTextAndCustomAttributesPassedDirectly() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $form = $this->factory->createNamed('name', TextType::class); $html = $this->renderLabel($form->createView(), 'Custom label', array( 'label_attr' => array( 'class' => 'my&class', @@ -114,7 +123,7 @@ public function testLabelWithCustomTextAndCustomAttributesPassedDirectly() public function testLabelWithCustomTextAsOptionAndCustomAttributesPassedDirectly() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array( + $form = $this->factory->createNamed('name', TextType::class, null, array( 'label' => 'Custom label', )); $html = $this->renderLabel($form->createView(), null, array( @@ -134,7 +143,7 @@ public function testLabelWithCustomTextAsOptionAndCustomAttributesPassedDirectly public function testLegendOnExpandedType() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + $form = $this->factory->createNamed('name', ChoiceType::class, null, array( 'label' => 'Custom label', 'expanded' => true, 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), @@ -153,7 +162,7 @@ public function testLegendOnExpandedType() public function testErrors() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $form = $this->factory->createNamed('name', TextType::class); $form->addError(new FormError('[trans]Error 1[/trans]')); $form->addError(new FormError('[trans]Error 2[/trans]')); $view = $form->createView(); @@ -178,7 +187,7 @@ public function testErrors() public function testErrorWithNoLabel() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', array('label'=>false)); + $form = $this->factory->createNamed('name', TextType::class, array('label' => false)); $form->addError(new FormError('[trans]Error 1[/trans]')); $view = $form->createView(); $html = $this->renderLabel($view); @@ -188,7 +197,7 @@ public function testErrorWithNoLabel() public function testCheckedCheckbox() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\CheckboxType', true); + $form = $this->factory->createNamed('name', CheckboxType::class, true); $this->assertWidgetMatchesXpath($form->createView(), array('id' => 'my&id', 'attr' => array('class' => 'my&class')), '/div @@ -205,7 +214,7 @@ public function testCheckedCheckbox() public function testSingleChoiceAttributesWithMainAttributes() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + $form = $this->factory->createNamed('name', ChoiceType::class, '&a', array( 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), 'multiple' => false, 'expanded' => false, @@ -228,7 +237,7 @@ public function testSingleChoiceAttributesWithMainAttributes() public function testSingleExpandedChoiceAttributesWithMainAttributes() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + $form = $this->factory->createNamed('name', ChoiceType::class, '&a', array( 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), 'multiple' => false, 'expanded' => true, @@ -261,7 +270,7 @@ public function testSingleExpandedChoiceAttributesWithMainAttributes() public function testUncheckedCheckbox() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\CheckboxType', false); + $form = $this->factory->createNamed('name', CheckboxType::class, false); $this->assertWidgetMatchesXpath($form->createView(), array('id' => 'my&id', 'attr' => array('class' => 'my&class')), '/div @@ -277,7 +286,7 @@ public function testUncheckedCheckbox() public function testCheckboxWithValue() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\CheckboxType', false, array( + $form = $this->factory->createNamed('name', CheckboxType::class, false, array( 'value' => 'foo&bar', )); @@ -295,7 +304,7 @@ public function testCheckboxWithValue() public function testSingleChoiceExpanded() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + $form = $this->factory->createNamed('name', ChoiceType::class, '&a', array( 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), 'multiple' => false, 'expanded' => true, @@ -326,7 +335,7 @@ public function testSingleChoiceExpanded() public function testSingleChoiceExpandedWithLabelsAsFalse() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + $form = $this->factory->createNamed('name', ChoiceType::class, '&a', array( 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), 'choice_label' => false, 'multiple' => false, @@ -356,7 +365,7 @@ public function testSingleChoiceExpandedWithLabelsAsFalse() public function testSingleChoiceExpandedWithLabelsSetByCallable() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + $form = $this->factory->createNamed('name', ChoiceType::class, '&a', array( 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), 'choice_label' => function ($choice, $label, $value) { if ('&b' === $choice) { @@ -400,7 +409,7 @@ public function testSingleChoiceExpandedWithLabelsSetByCallable() public function testSingleChoiceExpandedWithLabelsSetFalseByCallable() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + $form = $this->factory->createNamed('name', ChoiceType::class, '&a', array( 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), 'choice_label' => function () { return false; @@ -432,7 +441,7 @@ public function testSingleChoiceExpandedWithLabelsSetFalseByCallable() public function testSingleChoiceExpandedWithoutTranslation() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + $form = $this->factory->createNamed('name', ChoiceType::class, '&a', array( 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), 'multiple' => false, 'expanded' => true, @@ -464,7 +473,7 @@ public function testSingleChoiceExpandedWithoutTranslation() public function testSingleChoiceExpandedAttributes() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + $form = $this->factory->createNamed('name', ChoiceType::class, '&a', array( 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), 'choice_attr' => array('Choice&B' => array('class' => 'foo&bar')), 'multiple' => false, @@ -496,7 +505,7 @@ public function testSingleChoiceExpandedAttributes() public function testSingleChoiceExpandedWithPlaceholder() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + $form = $this->factory->createNamed('name', ChoiceType::class, '&a', array( 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), 'multiple' => false, 'expanded' => true, @@ -536,7 +545,7 @@ public function testSingleChoiceExpandedWithPlaceholder() public function testSingleChoiceExpandedWithPlaceholderWithoutTranslation() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + $form = $this->factory->createNamed('name', ChoiceType::class, '&a', array( 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), 'multiple' => false, 'expanded' => true, @@ -577,7 +586,7 @@ public function testSingleChoiceExpandedWithPlaceholderWithoutTranslation() public function testSingleChoiceExpandedWithBooleanValue() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', true, array( + $form = $this->factory->createNamed('name', ChoiceType::class, true, array( 'choices' => array('Choice&A' => '1', 'Choice&B' => '0'), 'multiple' => false, 'expanded' => true, @@ -608,7 +617,7 @@ public function testSingleChoiceExpandedWithBooleanValue() public function testMultipleChoiceExpanded() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a', '&c'), array( + $form = $this->factory->createNamed('name', ChoiceType::class, array('&a', '&c'), array( 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), 'multiple' => true, 'expanded' => true, @@ -647,7 +656,7 @@ public function testMultipleChoiceExpanded() public function testMultipleChoiceExpandedWithLabelsAsFalse() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array( + $form = $this->factory->createNamed('name', ChoiceType::class, array('&a'), array( 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), 'choice_label' => false, 'multiple' => true, @@ -677,7 +686,7 @@ public function testMultipleChoiceExpandedWithLabelsAsFalse() public function testMultipleChoiceExpandedWithLabelsSetByCallable() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array( + $form = $this->factory->createNamed('name', ChoiceType::class, array('&a'), array( 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), 'choice_label' => function ($choice, $label, $value) { if ('&b' === $choice) { @@ -721,7 +730,7 @@ public function testMultipleChoiceExpandedWithLabelsSetByCallable() public function testMultipleChoiceExpandedWithLabelsSetFalseByCallable() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array( + $form = $this->factory->createNamed('name', ChoiceType::class, array('&a'), array( 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), 'choice_label' => function () { return false; @@ -753,7 +762,7 @@ public function testMultipleChoiceExpandedWithLabelsSetFalseByCallable() public function testMultipleChoiceExpandedWithoutTranslation() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a', '&c'), array( + $form = $this->factory->createNamed('name', ChoiceType::class, array('&a', '&c'), array( 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), 'multiple' => true, 'expanded' => true, @@ -793,7 +802,7 @@ public function testMultipleChoiceExpandedWithoutTranslation() public function testMultipleChoiceExpandedAttributes() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a', '&c'), array( + $form = $this->factory->createNamed('name', ChoiceType::class, array('&a', '&c'), array( 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), 'choice_attr' => array('Choice&B' => array('class' => 'foo&bar')), 'multiple' => true, @@ -833,7 +842,7 @@ public function testMultipleChoiceExpandedAttributes() public function testCheckedRadio() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RadioType', true); + $form = $this->factory->createNamed('name', RadioType::class, true); $this->assertWidgetMatchesXpath($form->createView(), array('id' => 'my&id', 'attr' => array('class' => 'my&class')), '/div @@ -855,7 +864,7 @@ public function testCheckedRadio() public function testUncheckedRadio() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RadioType', false); + $form = $this->factory->createNamed('name', RadioType::class, false); $this->assertWidgetMatchesXpath($form->createView(), array('id' => 'my&id', 'attr' => array('class' => 'my&class')), '/div @@ -876,7 +885,7 @@ public function testUncheckedRadio() public function testRadioWithValue() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RadioType', false, array( + $form = $this->factory->createNamed('name', RadioType::class, false, array( 'value' => 'foo&bar', )); @@ -900,7 +909,7 @@ public function testRadioWithValue() public function testButtonAttributeNameRepeatedIfTrue() { - $form = $this->factory->createNamed('button', 'Symfony\Component\Form\Extension\Core\Type\ButtonType', null, array( + $form = $this->factory->createNamed('button', ButtonType::class, null, array( 'attr' => array('foo' => true), )); @@ -912,7 +921,7 @@ public function testButtonAttributeNameRepeatedIfTrue() public function testFile() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\FileType'); + $form = $this->factory->createNamed('name', FileType::class); $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class form-control-file')), '/input @@ -923,7 +932,7 @@ public function testFile() public function testMoney() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\MoneyType', 1234.56, array( + $form = $this->factory->createNamed('name', MoneyType::class, 1234.56, array( 'currency' => 'EUR', )); @@ -951,7 +960,7 @@ public function testMoney() public function testPercent() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\PercentType', 0.1); + $form = $this->factory->createNamed('name', PercentType::class, 0.1); $this->assertWidgetMatchesXpath($form->createView(), array('id' => 'my&id', 'attr' => array('class' => 'my&class')), '/div diff --git a/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php b/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php index d1e7af206804b..0424850e144c2 100644 --- a/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php +++ b/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php @@ -58,7 +58,7 @@ public function createArgumentMetadata($controller) } foreach ($reflection->getParameters() as $param) { - $arguments[] = new ArgumentMetadata($param->getName(), $this->getType($param), $this->isVariadic($param), $this->hasDefaultValue($param), $this->getDefaultValue($param), $param->allowsNull()); + $arguments[] = new ArgumentMetadata($param->getName(), $this->getType($param, $reflection), $this->isVariadic($param), $this->hasDefaultValue($param), $this->getDefaultValue($param), $param->allowsNull()); } return $arguments; @@ -107,23 +107,35 @@ private function getDefaultValue(\ReflectionParameter $parameter) * * @return null|string */ - private function getType(\ReflectionParameter $parameter) + private function getType(\ReflectionParameter $parameter, \ReflectionFunctionAbstract $function) { if ($this->supportsParameterType) { if (!$type = $parameter->getType()) { return; } - $typeName = $type instanceof \ReflectionNamedType ? $type->getName() : $type->__toString(); - if ('array' === $typeName && !$type->isBuiltin()) { + $name = $type instanceof \ReflectionNamedType ? $type->getName() : $type->__toString(); + if ('array' === $name && !$type->isBuiltin()) { // Special case for HHVM with variadics return; } - - return $typeName; + } elseif (preg_match('/^(?:[^ ]++ ){4}([a-zA-Z_\x7F-\xFF][^ ]++)/', $parameter, $name)) { + $name = $name[1]; + } else { + return; } + $lcName = strtolower($name); - if (preg_match('/^(?:[^ ]++ ){4}([a-zA-Z_\x7F-\xFF][^ ]++)/', $parameter, $info)) { - return $info[1]; + if ('self' !== $lcName && 'parent' !== $lcName) { + return $name; + } + if (!$function instanceof \ReflectionMethod) { + return; + } + if ('self' === $lcName) { + return $function->getDeclaringClass()->name; + } + if ($parent = $function->getDeclaringClass()->getParentClass()) { + return $parent->name; } } } diff --git a/src/Symfony/Component/HttpKernel/Profiler/Profiler.php b/src/Symfony/Component/HttpKernel/Profiler/Profiler.php index 15bfbc5a07777..6245a21b5f142 100644 --- a/src/Symfony/Component/HttpKernel/Profiler/Profiler.php +++ b/src/Symfony/Component/HttpKernel/Profiler/Profiler.php @@ -37,7 +37,7 @@ class Profiler private $enabled = true; /** - * @param bool $enable The initial enabled state + * @param bool $enable The initial enabled state */ public function __construct(ProfilerStorageInterface $storage, LoggerInterface $logger = null, $enable = true) { diff --git a/src/Symfony/Component/HttpKernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php b/src/Symfony/Component/HttpKernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php index b4b449f358611..bf0db5ad56512 100644 --- a/src/Symfony/Component/HttpKernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php @@ -126,11 +126,11 @@ public function testNullableTypesSignature() ), $arguments); } - private function signature1(ArgumentMetadataFactoryTest $foo, array $bar, callable $baz) + private function signature1(self $foo, array $bar, callable $baz) { } - private function signature2(ArgumentMetadataFactoryTest $foo = null, FakeClassThatDoesNotExist $bar = null, ImportedAndFake $baz = null) + private function signature2(self $foo = null, FakeClassThatDoesNotExist $bar = null, ImportedAndFake $baz = null) { } diff --git a/src/Symfony/Component/HttpKernel/Tests/Profiler/ProfilerTest.php b/src/Symfony/Component/HttpKernel/Tests/Profiler/ProfilerTest.php index 243c3c5c5a7cb..2d5f0ca5ad59e 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Profiler/ProfilerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Profiler/ProfilerTest.php @@ -44,7 +44,7 @@ public function testCollect() public function testReset() { $collector = $this->getMockBuilder(DataCollectorInterface::class) - ->setMethods(['collect', 'getName', 'reset']) + ->setMethods(array('collect', 'getName', 'reset')) ->getMock(); $collector->expects($this->any())->method('getName')->willReturn('mock'); $collector->expects($this->once())->method('reset'); diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/ParentDummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/ParentDummy.php index 330496827cfc4..8d5c4fe107162 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/ParentDummy.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/ParentDummy.php @@ -29,7 +29,7 @@ class ParentDummy public $foo2; /** - * @var callback + * @var callable */ public $foo3; diff --git a/src/Symfony/Component/PropertyInfo/Type.php b/src/Symfony/Component/PropertyInfo/Type.php index 86bc2f7643db4..b2c8e36be44e0 100644 --- a/src/Symfony/Component/PropertyInfo/Type.php +++ b/src/Symfony/Component/PropertyInfo/Type.php @@ -61,12 +61,10 @@ class Type * @param bool $nullable * @param string|null $class * @param bool $collection - * @param Type|null $collectionKeyType - * @param Type|null $collectionValueType * * @throws \InvalidArgumentException */ - public function __construct($builtinType, $nullable = false, $class = null, $collection = false, Type $collectionKeyType = null, Type $collectionValueType = null) + public function __construct($builtinType, $nullable = false, $class = null, $collection = false, self $collectionKeyType = null, self $collectionValueType = null) { if (!in_array($builtinType, self::$builtinTypes)) { throw new \InvalidArgumentException(sprintf('"%s" is not a valid PHP type.', $builtinType)); diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php b/src/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php index 6916297b8c174..dd057d2ee8301 100644 --- a/src/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php +++ b/src/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php @@ -106,7 +106,7 @@ protected function getParent() /** * Sets the parent collection. */ - protected function setParent(DumperCollection $parent) + protected function setParent(self $parent) { $this->parent = $parent; } diff --git a/src/Symfony/Component/Routing/RouteCollectionBuilder.php b/src/Symfony/Component/Routing/RouteCollectionBuilder.php index e8a9a165d6734..3aec146951314 100644 --- a/src/Symfony/Component/Routing/RouteCollectionBuilder.php +++ b/src/Symfony/Component/Routing/RouteCollectionBuilder.php @@ -118,7 +118,7 @@ public function createBuilder() * @param string $prefix * @param RouteCollectionBuilder $builder */ - public function mount($prefix, RouteCollectionBuilder $builder) + public function mount($prefix, self $builder) { $builder->prefix = trim(trim($prefix), '/'); $this->routes[] = $builder; @@ -251,8 +251,6 @@ public function setMethods($methods) /** * Adds a resource for this collection. * - * @param ResourceInterface $resource - * * @return $this */ private function addResource(ResourceInterface $resource) diff --git a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php index 5b74523d631b0..9b3bfedcc8121 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php @@ -46,12 +46,8 @@ class ContextListener implements ListenerInterface private $logoutOnUserChange = false; /** - * @param TokenStorageInterface $tokenStorage - * @param iterable|UserProviderInterface[] $userProviders - * @param string $contextKey - * @param LoggerInterface|null $logger - * @param EventDispatcherInterface|null $dispatcher - * @param AuthenticationTrustResolverInterface|null $trustResolver + * @param iterable|UserProviderInterface[] $userProviders + * @param string $contextKey */ public function __construct(TokenStorageInterface $tokenStorage, $userProviders, $contextKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, AuthenticationTrustResolverInterface $trustResolver = null) { diff --git a/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php b/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php index 944a7b3133f35..d9a15d5ac0de5 100644 --- a/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php +++ b/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php @@ -60,5 +60,5 @@ public function getMaxDepth(); /** * Merges an {@see AttributeMetadataInterface} with in the current one. */ - public function merge(AttributeMetadataInterface $attributeMetadata); + public function merge(self $attributeMetadata); } diff --git a/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php b/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php index 3811e56548a0c..a0765861643bb 100644 --- a/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php +++ b/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php @@ -46,7 +46,7 @@ public function getAttributesMetadata(); /** * Merges a {@link ClassMetadataInterface} in the current one. */ - public function merge(ClassMetadataInterface $classMetadata); + public function merge(self $classMetadata); /** * Returns a {@link \ReflectionClass} instance for this class. diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 25d6ff6a9d64e..ec03fef8cfbc6 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -301,7 +301,6 @@ private function validateAndDenormalize($currentClass, $attribute, $data, $forma /** * Sets an attribute and apply the name converter if necessary. * - * @param array $data * @param string $attribute * @param mixed $attributeValue * diff --git a/src/Symfony/Component/Translation/MessageCatalogueInterface.php b/src/Symfony/Component/Translation/MessageCatalogueInterface.php index 4dad27fbf6aa3..e0dbb2bd962cb 100644 --- a/src/Symfony/Component/Translation/MessageCatalogueInterface.php +++ b/src/Symfony/Component/Translation/MessageCatalogueInterface.php @@ -105,7 +105,7 @@ public function add($messages, $domain = 'messages'); * * The two catalogues must have the same locale. */ - public function addCatalogue(MessageCatalogueInterface $catalogue); + public function addCatalogue(self $catalogue); /** * Merges translations from the given Catalogue into the current one @@ -113,7 +113,7 @@ public function addCatalogue(MessageCatalogueInterface $catalogue); * * This is used to provide default translations when they do not exist for the current locale. */ - public function addFallbackCatalogue(MessageCatalogueInterface $catalogue); + public function addFallbackCatalogue(self $catalogue); /** * Gets the fallback catalogue. diff --git a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php index f716ce3432398..84b0e3f4f7731 100644 --- a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php +++ b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php @@ -335,7 +335,7 @@ public function addGetterMethodConstraints($property, $method, array $constraint /** * Merges the constraints of the given metadata into this object. */ - public function mergeConstraints(ClassMetadata $source) + public function mergeConstraints(self $source) { if ($source->isGroupSequenceProvider()) { $this->setGroupSequenceProvider(true); From 198925ee4e573fc9610aebe230af3bb54fa66e2c Mon Sep 17 00:00:00 2001 From: Tobias Schultze Date: Sat, 21 Apr 2018 21:29:51 +0200 Subject: [PATCH 60/76] [Messenger] implement several senders using a ChainSender --- .../DependencyInjection/Configuration.php | 7 +- .../FrameworkExtension.php | 19 ++++- .../Resources/config/messenger.xml | 3 +- .../Resources/config/schema/symfony-1.0.xsd | 1 + .../Fixtures/php/messenger_routing.php | 7 +- .../Fixtures/xml/messenger_routing.xml | 4 +- .../Fixtures/yml/messenger_routing.yml | 6 +- .../FrameworkExtensionTest.php | 13 ++- .../Middleware/SendMessageMiddleware.php | 23 +++-- .../Asynchronous/Routing/SenderLocator.php | 44 +++++----- .../Routing/SenderLocatorInterface.php | 5 +- .../Middleware/SendMessageMiddlewareTest.php | 83 ++++++++++++++----- .../Routing/SenderLocatorTest.php | 41 ++++----- .../Messenger/Transport/ChainSender.php | 38 +++++++++ 14 files changed, 204 insertions(+), 90 deletions(-) create mode 100644 src/Symfony/Component/Messenger/Transport/ChainSender.php diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index ce65f52bef128..690826cc432fd 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -986,7 +986,10 @@ private function addMessengerSection(ArrayNodeDefinition $rootNode) $newConfig = array(); foreach ($config as $k => $v) { if (!\is_int($k)) { - $newConfig[$k] = array('senders' => \is_array($v) ? array_values($v) : array($v)); + $newConfig[$k] = array( + 'senders' => $v['senders'] ?? (\is_array($v) ? array_values($v) : array($v)), + 'send_and_handle' => $v['send_and_handle'] ?? false, + ); } else { $newConfig[$v['message-class']]['senders'] = array_map( function ($a) { @@ -994,6 +997,7 @@ function ($a) { }, array_values($v['sender']) ); + $newConfig[$v['message-class']]['send-and-handle'] = $v['send-and-handle'] ?? false; } } @@ -1006,6 +1010,7 @@ function ($a) { ->requiresAtLeastOneElement() ->prototype('scalar')->end() ->end() + ->booleanNode('send_and_handle')->defaultFalse()->end() ->end() ->end() ->end() diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 1d69a4f99f4a6..e8dfbd14a1695 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -63,6 +63,7 @@ use Symfony\Component\Messenger\Handler\MessageHandlerInterface; use Symfony\Component\Messenger\MessageBus; use Symfony\Component\Messenger\MessageBusInterface; +use Symfony\Component\Messenger\Transport\ChainSender; use Symfony\Component\Messenger\Transport\TransportFactoryInterface; use Symfony\Component\Messenger\Transport\TransportInterface; use Symfony\Component\PropertyAccess\PropertyAccessor; @@ -1494,16 +1495,28 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder throw new LogicException(sprintf('The default bus named "%s" is not defined. Define it or change the default bus name.', $config['default_bus'])); } - $messageToSenderIdsMapping = array(); + $messageToSenderIdMapping = array(); + $messageToSendAndHandleMapping = array(); foreach ($config['routing'] as $message => $messageConfiguration) { if ('*' !== $message && !class_exists($message) && !interface_exists($message, false)) { throw new LogicException(sprintf('Messenger routing configuration contains a mistake: message "%s" does not exist. It needs to match an existing class or interface.', $message)); } - $messageToSenderIdsMapping[$message] = $messageConfiguration['senders']; + if (1 < \count($messageConfiguration['senders'])) { + $senders = array_map(function ($sender) { return new Reference($sender); }, $messageConfiguration['senders']); + $chainSenderDefinition = new Definition(ChainSender::class, array($senders)); + $chainSenderId = '.messenger.chain_sender.'.$message; + $container->setDefinition($chainSenderId, $chainSenderDefinition); + $messageToSenderIdMapping[$message] = $chainSenderId; + } else { + $messageToSenderIdMapping[$message] = $messageConfiguration['senders'][0]; + } + + $messageToSendAndHandleMapping[$message] = $messageConfiguration['send_and_handle']; } - $container->getDefinition('messenger.asynchronous.routing.sender_locator')->replaceArgument(1, $messageToSenderIdsMapping); + $container->getDefinition('messenger.asynchronous.routing.sender_locator')->replaceArgument(1, $messageToSenderIdMapping); + $container->getDefinition('messenger.middleware.route_messages')->replaceArgument(1, $messageToSendAndHandleMapping); foreach ($config['transports'] as $name => $transport) { if (0 === strpos($transport['dsn'], 'amqp://') && !$container->hasDefinition('messenger.transport.amqp.factory')) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml index 145f0543de9ed..32a4a4d895351 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml @@ -15,10 +15,11 @@ - + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd index c5e05b11a7ecc..2dbe54b5d938c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd @@ -375,6 +375,7 @@ + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_routing.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_routing.php index 29891140ffdff..51b1184764dfe 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_routing.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_routing.php @@ -3,8 +3,11 @@ $container->loadFromExtension('framework', array( 'messenger' => array( 'routing' => array( - 'Symfony\Component\Messenger\Tests\Fixtures\DummyMessage' => array('amqp'), - 'Symfony\Component\Messenger\Tests\Fixtures\SecondMessage' => array('amqp', 'audit', null), + 'Symfony\Component\Messenger\Tests\Fixtures\DummyMessage' => array('amqp', 'audit'), + 'Symfony\Component\Messenger\Tests\Fixtures\SecondMessage' => array( + 'senders' => array('amqp', 'audit'), + 'send_and_handle' => true, + ), '*' => 'amqp', ), ), diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_routing.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_routing.xml index 3772030e5e3e1..808b0ba9cd46b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_routing.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_routing.xml @@ -9,11 +9,11 @@ + - + - diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_routing.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_routing.yml index 2243a76f23efd..da096fcaed645 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_routing.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_routing.yml @@ -1,6 +1,8 @@ framework: messenger: routing: - 'Symfony\Component\Messenger\Tests\Fixtures\DummyMessage': amqp - 'Symfony\Component\Messenger\Tests\Fixtures\SecondMessage': [amqp, audit, ~] + 'Symfony\Component\Messenger\Tests\Fixtures\DummyMessage': [amqp, audit] + 'Symfony\Component\Messenger\Tests\Fixtures\SecondMessage': + senders: [amqp, audit] + send_and_handle: true '*': amqp diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index 383dbd7ae0c1a..2ae8411c22909 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -558,14 +558,21 @@ public function testMessengerRouting() { $container = $this->createContainerFromFile('messenger_routing'); $senderLocatorDefinition = $container->getDefinition('messenger.asynchronous.routing.sender_locator'); + $sendMessageMiddlewareDefinition = $container->getDefinition('messenger.middleware.route_messages'); $messageToSenderIdsMapping = array( - DummyMessage::class => array('amqp'), - SecondMessage::class => array('amqp', 'audit', null), - '*' => array('amqp'), + DummyMessage::class => '.messenger.chain_sender.'.DummyMessage::class, + SecondMessage::class => '.messenger.chain_sender.'.SecondMessage::class, + '*' => 'amqp', + ); + $messageToSendAndHandleMapping = array( + DummyMessage::class => false, + SecondMessage::class => true, + '*' => false, ); $this->assertSame($messageToSenderIdsMapping, $senderLocatorDefinition->getArgument(1)); + $this->assertSame($messageToSendAndHandleMapping, $sendMessageMiddlewareDefinition->getArgument(1)); } /** diff --git a/src/Symfony/Component/Messenger/Asynchronous/Middleware/SendMessageMiddleware.php b/src/Symfony/Component/Messenger/Asynchronous/Middleware/SendMessageMiddleware.php index 9aabe794a11ab..16fdfd81f1083 100644 --- a/src/Symfony/Component/Messenger/Asynchronous/Middleware/SendMessageMiddleware.php +++ b/src/Symfony/Component/Messenger/Asynchronous/Middleware/SendMessageMiddleware.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Messenger\Asynchronous\Middleware; +use Symfony\Component\Messenger\Asynchronous\Routing\SenderLocator; use Symfony\Component\Messenger\Asynchronous\Routing\SenderLocatorInterface; use Symfony\Component\Messenger\Asynchronous\Transport\ReceivedMessage; use Symfony\Component\Messenger\Envelope; @@ -19,14 +20,17 @@ /** * @author Samuel Roze + * @author Tobias Schultze */ class SendMessageMiddleware implements MiddlewareInterface, EnvelopeAwareInterface { private $senderLocator; + private $messagesToSendAndHandleMapping; - public function __construct(SenderLocatorInterface $senderLocator) + public function __construct(SenderLocatorInterface $senderLocator, array $messagesToSendAndHandleMapping = array()) { $this->senderLocator = $senderLocator; + $this->messagesToSendAndHandleMapping = $messagesToSendAndHandleMapping; } /** @@ -40,20 +44,21 @@ public function handle($message, callable $next) return $next($message); } - if (!empty($senders = $this->senderLocator->getSendersForMessage($envelope->getMessage()))) { - foreach ($senders as $sender) { - if (null === $sender) { - continue; - } + $sender = $this->senderLocator->getSenderForMessage($envelope->getMessage()); - $sender->send($envelope); - } + if ($sender) { + $sender->send($envelope); - if (!\in_array(null, $senders, true)) { + if (!$this->mustSendAndHandle($envelope->getMessage())) { return; } } return $next($message); } + + private function mustSendAndHandle($message): bool + { + return (bool) SenderLocator::getValueFromMessageRouting($this->messagesToSendAndHandleMapping, $message); + } } diff --git a/src/Symfony/Component/Messenger/Asynchronous/Routing/SenderLocator.php b/src/Symfony/Component/Messenger/Asynchronous/Routing/SenderLocator.php index 506fe234c6cef..e85453691bd92 100644 --- a/src/Symfony/Component/Messenger/Asynchronous/Routing/SenderLocator.php +++ b/src/Symfony/Component/Messenger/Asynchronous/Routing/SenderLocator.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Messenger\Asynchronous\Routing; use Psr\Container\ContainerInterface; +use Symfony\Component\Messenger\Transport\SenderInterface; /** * @author Samuel Roze @@ -19,42 +20,47 @@ class SenderLocator implements SenderLocatorInterface { private $senderServiceLocator; - private $messageToSenderIdsMapping; + private $messageToSenderIdMapping; - public function __construct(ContainerInterface $senderServiceLocator, array $messageToSenderIdsMapping) + public function __construct(ContainerInterface $senderServiceLocator, array $messageToSenderIdMapping) { $this->senderServiceLocator = $senderServiceLocator; - $this->messageToSenderIdsMapping = $messageToSenderIdsMapping; + $this->messageToSenderIdMapping = $messageToSenderIdMapping; } /** * {@inheritdoc} */ - public function getSendersForMessage($message): array + public function getSenderForMessage($message): ?SenderInterface { - $senders = array(); - foreach ($this->getSenderIds($message) as $senderId) { - $senders[] = $this->senderServiceLocator->get($senderId); - } + $senderId = $this->getSenderId($message); + + return $senderId ? $this->senderServiceLocator->get($senderId) : null; + } - return $senders; + private function getSenderId($message): ?string + { + return self::getValueFromMessageRouting($this->messageToSenderIdMapping, $message); } - private function getSenderIds($message): array + /** + * @internal + */ + public static function getValueFromMessageRouting(array $mapping, $message) { - if (isset($this->messageToSenderIdsMapping[\get_class($message)])) { - return $this->messageToSenderIdsMapping[\get_class($message)]; + if (isset($mapping[\get_class($message)])) { + return $mapping[\get_class($message)]; } - if ($messageToSenderIdsMapping = array_intersect_key($this->messageToSenderIdsMapping, class_parents($message))) { - return current($messageToSenderIdsMapping); + if ($parentsMapping = array_intersect_key($mapping, class_parents($message))) { + return current($parentsMapping); } - if ($messageToSenderIdsMapping = array_intersect_key($this->messageToSenderIdsMapping, class_implements($message))) { - return current($messageToSenderIdsMapping); + if ($interfaceMapping = array_intersect_key($mapping, class_implements($message))) { + return current($interfaceMapping); } - if (isset($this->messageToSenderIdsMapping['*'])) { - return $this->messageToSenderIdsMapping['*']; + if (isset($mapping['*'])) { + return $mapping['*']; } - return array(); + return null; } } diff --git a/src/Symfony/Component/Messenger/Asynchronous/Routing/SenderLocatorInterface.php b/src/Symfony/Component/Messenger/Asynchronous/Routing/SenderLocatorInterface.php index d97508b31a2f1..ffce0cf3a8554 100644 --- a/src/Symfony/Component/Messenger/Asynchronous/Routing/SenderLocatorInterface.php +++ b/src/Symfony/Component/Messenger/Asynchronous/Routing/SenderLocatorInterface.php @@ -15,6 +15,7 @@ /** * @author Samuel Roze + * @author Tobias Schultze * * @experimental in 4.1 */ @@ -25,7 +26,7 @@ interface SenderLocatorInterface * * @param object $message * - * @return SenderInterface[] + * @return SenderInterface|null */ - public function getSendersForMessage($message): array; + public function getSenderForMessage($message): ?SenderInterface; } diff --git a/src/Symfony/Component/Messenger/Tests/Asynchronous/Middleware/SendMessageMiddlewareTest.php b/src/Symfony/Component/Messenger/Tests/Asynchronous/Middleware/SendMessageMiddlewareTest.php index aa22741c397b6..c9d548360c7b0 100644 --- a/src/Symfony/Component/Messenger/Tests/Asynchronous/Middleware/SendMessageMiddlewareTest.php +++ b/src/Symfony/Component/Messenger/Tests/Asynchronous/Middleware/SendMessageMiddlewareTest.php @@ -16,7 +16,9 @@ use Symfony\Component\Messenger\Asynchronous\Routing\SenderLocatorInterface; use Symfony\Component\Messenger\Asynchronous\Transport\ReceivedMessage; use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Tests\Asynchronous\Routing\ChildDummyMessage; use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; +use Symfony\Component\Messenger\Tests\Fixtures\DummyMessageInterface; use Symfony\Component\Messenger\Transport\SenderInterface; class SendMessageMiddlewareTest extends TestCase @@ -27,9 +29,7 @@ public function testItSendsTheMessageToAssignedSender() $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); $next = $this->createPartialMock(\stdClass::class, array('__invoke')); - $middleware = new SendMessageMiddleware(new InMemorySenderLocator(array( - $sender, - ))); + $middleware = new SendMessageMiddleware(new InMemorySenderLocator($sender)); $sender->expects($this->once())->method('send')->with(Envelope::wrap($message)); $next->expects($this->never())->method($this->anything()); @@ -43,9 +43,7 @@ public function testItSendsTheMessageToAssignedSenderWithPreWrappedMessage() $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); $next = $this->createPartialMock(\stdClass::class, array('__invoke')); - $middleware = new SendMessageMiddleware(new InMemorySenderLocator(array( - $sender, - ))); + $middleware = new SendMessageMiddleware(new InMemorySenderLocator($sender)); $sender->expects($this->once())->method('send')->with($envelope); $next->expects($this->never())->method($this->anything()); @@ -53,16 +51,63 @@ public function testItSendsTheMessageToAssignedSenderWithPreWrappedMessage() $middleware->handle($envelope, $next); } - public function testItAlsoCallsTheNextMiddlewareIfASenderIsNull() + public function testItAlsoCallsTheNextMiddlewareBasedOnTheMessageClass() { $message = new DummyMessage('Hey'); $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); $next = $this->createPartialMock(\stdClass::class, array('__invoke')); - $middleware = new SendMessageMiddleware(new InMemorySenderLocator(array( - $sender, - null, - ))); + $middleware = new SendMessageMiddleware(new InMemorySenderLocator($sender), array( + DummyMessage::class => true, + )); + + $sender->expects($this->once())->method('send')->with(Envelope::wrap($message)); + $next->expects($this->once())->method($this->anything()); + + $middleware->handle($message, $next); + } + + public function testItAlsoCallsTheNextMiddlewareBasedOnTheMessageParentClass() + { + $message = new ChildDummyMessage('Hey'); + $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); + $next = $this->createPartialMock(\stdClass::class, array('__invoke')); + + $middleware = new SendMessageMiddleware(new InMemorySenderLocator($sender), array( + DummyMessage::class => true, + )); + + $sender->expects($this->once())->method('send')->with(Envelope::wrap($message)); + $next->expects($this->once())->method($this->anything()); + + $middleware->handle($message, $next); + } + + public function testItAlsoCallsTheNextMiddlewareBasedOnTheMessageInterface() + { + $message = new DummyMessage('Hey'); + $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); + $next = $this->createPartialMock(\stdClass::class, array('__invoke')); + + $middleware = new SendMessageMiddleware(new InMemorySenderLocator($sender), array( + DummyMessageInterface::class => true, + )); + + $sender->expects($this->once())->method('send')->with(Envelope::wrap($message)); + $next->expects($this->once())->method($this->anything()); + + $middleware->handle($message, $next); + } + + public function testItAlsoCallsTheNextMiddlewareBasedOnWildcard() + { + $message = new DummyMessage('Hey'); + $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); + $next = $this->createPartialMock(\stdClass::class, array('__invoke')); + + $middleware = new SendMessageMiddleware(new InMemorySenderLocator($sender), array( + '*' => true, + )); $sender->expects($this->once())->method('send')->with(Envelope::wrap($message)); $next->expects($this->once())->method($this->anything()); @@ -75,7 +120,7 @@ public function testItCallsTheNextMiddlewareWhenNoSenderForThisMessage() $message = new DummyMessage('Hey'); $next = $this->createPartialMock(\stdClass::class, array('__invoke')); - $middleware = new SendMessageMiddleware(new InMemorySenderLocator(array())); + $middleware = new SendMessageMiddleware(new InMemorySenderLocator(null)); $next->expects($this->once())->method($this->anything()); @@ -89,9 +134,7 @@ public function testItSkipsReceivedMessages() $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); $next = $this->createPartialMock(\stdClass::class, array('__invoke')); - $middleware = new SendMessageMiddleware(new InMemorySenderLocator(array( - $sender, - ))); + $middleware = new SendMessageMiddleware(new InMemorySenderLocator($sender)); $sender->expects($this->never())->method('send'); $next->expects($this->once())->method('__invoke')->with($envelope); @@ -102,15 +145,15 @@ public function testItSkipsReceivedMessages() class InMemorySenderLocator implements SenderLocatorInterface { - private $senders; + private $sender; - public function __construct(array $senders) + public function __construct(?SenderInterface $sender) { - $this->senders = $senders; + $this->sender = $sender; } - public function getSendersForMessage($message): array + public function getSenderForMessage($message): ?SenderInterface { - return $this->senders; + return $this->sender; } } diff --git a/src/Symfony/Component/Messenger/Tests/Asynchronous/Routing/SenderLocatorTest.php b/src/Symfony/Component/Messenger/Tests/Asynchronous/Routing/SenderLocatorTest.php index 6278be85c57dc..22cf6bac81e67 100644 --- a/src/Symfony/Component/Messenger/Tests/Asynchronous/Routing/SenderLocatorTest.php +++ b/src/Symfony/Component/Messenger/Tests/Asynchronous/Routing/SenderLocatorTest.php @@ -28,13 +28,11 @@ public function testItReturnsTheSenderBasedOnTheMessageClass() $container->set('my_amqp_sender', $sender); $locator = new SenderLocator($container, array( - DummyMessage::class => array( - 'my_amqp_sender', - ), + DummyMessage::class => 'my_amqp_sender', )); - $this->assertSame(array($sender), $locator->getSendersForMessage(new DummyMessage('Hello'))); - $this->assertSame(array(), $locator->getSendersForMessage(new SecondMessage())); + $this->assertSame($sender, $locator->getSenderForMessage(new DummyMessage('Hello'))); + $this->assertNull($locator->getSenderForMessage(new SecondMessage())); } public function testItReturnsTheSenderBasedOnTheMessageParentClass() @@ -48,15 +46,12 @@ public function testItReturnsTheSenderBasedOnTheMessageParentClass() $container->set('my_api_sender', $apiSender); $locator = new SenderLocator($container, array( - DummyMessageInterface::class => array( - 'my_api_sender', - ), - DummyMessage::class => array( - 'my_amqp_sender', - ), + DummyMessageInterface::class => 'my_api_sender', + DummyMessage::class => 'my_amqp_sender', )); - $this->assertSame(array($sender), $locator->getSendersForMessage(new ChildDummyMessage('Hello'))); - $this->assertSame(array(), $locator->getSendersForMessage(new SecondMessage())); + + $this->assertSame($sender, $locator->getSenderForMessage(new ChildDummyMessage('Hello'))); + $this->assertNull($locator->getSenderForMessage(new SecondMessage())); } public function testItReturnsTheSenderBasedOnTheMessageInterface() @@ -67,13 +62,11 @@ public function testItReturnsTheSenderBasedOnTheMessageInterface() $container->set('my_amqp_sender', $sender); $locator = new SenderLocator($container, array( - DummyMessageInterface::class => array( - 'my_amqp_sender', - ), + DummyMessageInterface::class => 'my_amqp_sender', )); - $this->assertSame(array($sender), $locator->getSendersForMessage(new DummyMessage('Hello'))); - $this->assertSame(array(), $locator->getSendersForMessage(new SecondMessage())); + $this->assertSame($sender, $locator->getSenderForMessage(new DummyMessage('Hello'))); + $this->assertNull($locator->getSenderForMessage(new SecondMessage())); } public function testItSupportsAWildcardInsteadOfTheMessageClass() @@ -87,16 +80,12 @@ public function testItSupportsAWildcardInsteadOfTheMessageClass() $container->set('my_api_sender', $apiSender); $locator = new SenderLocator($container, array( - DummyMessage::class => array( - 'my_amqp_sender', - ), - '*' => array( - 'my_api_sender', - ), + DummyMessage::class => 'my_amqp_sender', + '*' => 'my_api_sender', )); - $this->assertSame(array($sender), $locator->getSendersForMessage(new DummyMessage('Hello'))); - $this->assertSame(array($apiSender), $locator->getSendersForMessage(new SecondMessage())); + $this->assertSame($sender, $locator->getSenderForMessage(new DummyMessage('Hello'))); + $this->assertSame($apiSender, $locator->getSenderForMessage(new SecondMessage())); } } diff --git a/src/Symfony/Component/Messenger/Transport/ChainSender.php b/src/Symfony/Component/Messenger/Transport/ChainSender.php new file mode 100644 index 0000000000000..97e3ce44bbaf1 --- /dev/null +++ b/src/Symfony/Component/Messenger/Transport/ChainSender.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Transport; + +/** + * @author Tobias Schultze + */ +class ChainSender implements SenderInterface +{ + private $senders; + + /** + * @param SenderInterface[] $senders + */ + public function __construct(iterable $senders) + { + $this->senders = $senders; + } + + /** + * {@inheritdoc} + */ + public function send($message): void + { + foreach ($this->senders as $sender) { + $sender->send($message); + } + } +} From 88a3b90860117f58f016d14f1dcad413b3ecdeb1 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 16 May 2018 15:47:24 +0200 Subject: [PATCH 61/76] [PropertyInfo] fix resolving parent|self type hints --- .../Extractor/ReflectionExtractor.php | 24 +++++++++++++------ .../PhpDocExtractorTest.php | 2 +- .../ReflectionExtractorTest.php | 6 +++++ .../SerializerExtractorTest.php | 2 +- .../PropertyInfo/Tests/Fixtures/Dummy.php | 14 +++++++++++ .../PropertyInfo/Tests/Fixtures/Php7Dummy.php | 10 +++++++- 6 files changed, 48 insertions(+), 10 deletions(-) rename src/Symfony/Component/PropertyInfo/Tests/{Extractors => Extractor}/PhpDocExtractorTest.php (99%) rename src/Symfony/Component/PropertyInfo/Tests/{Extractors => Extractor}/ReflectionExtractorTest.php (93%) rename src/Symfony/Component/PropertyInfo/Tests/{Extractors => Extractor}/SerializerExtractorTest.php (95%) diff --git a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php index bc9ae5074d08c..4e527eb05db12 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php @@ -176,7 +176,7 @@ private function extractFromMutator($class, $property) if (!$reflectionType = $reflectionParameter->getType()) { return; } - $type = $this->extractFromReflectionType($reflectionType); + $type = $this->extractFromReflectionType($reflectionType, $reflectionMethod); // HHVM reports variadics with "array" but not builtin type hints if (!$reflectionType->isBuiltin() && Type::BUILTIN_TYPE_ARRAY === $type->getBuiltinType()) { @@ -188,7 +188,7 @@ private function extractFromMutator($class, $property) } elseif (Type::BUILTIN_TYPE_CALLABLE === $info[1]) { $type = new Type(Type::BUILTIN_TYPE_CALLABLE, $reflectionParameter->allowsNull()); } else { - $type = new Type(Type::BUILTIN_TYPE_OBJECT, $reflectionParameter->allowsNull(), $info[1]); + $type = new Type(Type::BUILTIN_TYPE_OBJECT, $reflectionParameter->allowsNull(), $this->resolveTypeName($info[1], $reflectionMethod)); } } else { return; @@ -217,7 +217,7 @@ private function extractFromAccessor($class, $property) } if ($this->supportsParameterType && $reflectionType = $reflectionMethod->getReturnType()) { - return array($this->extractFromReflectionType($reflectionType)); + return array($this->extractFromReflectionType($reflectionType, $reflectionMethod)); } if (\in_array($prefix, array('is', 'can'))) { @@ -228,11 +228,9 @@ private function extractFromAccessor($class, $property) /** * Extracts data from the PHP 7 reflection type. * - * @param \ReflectionType $reflectionType - * * @return Type */ - private function extractFromReflectionType(\ReflectionType $reflectionType) + private function extractFromReflectionType(\ReflectionType $reflectionType, \ReflectionMethod $reflectionMethod) { $phpTypeOrClass = $reflectionType instanceof \ReflectionNamedType ? $reflectionType->getName() : $reflectionType->__toString(); $nullable = $reflectionType->allowsNull(); @@ -244,12 +242,24 @@ private function extractFromReflectionType(\ReflectionType $reflectionType) } elseif ($reflectionType->isBuiltin()) { $type = new Type($phpTypeOrClass, $nullable); } else { - $type = new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $phpTypeOrClass); + $type = new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $this->resolveTypeName($phpTypeOrClass, $reflectionMethod)); } return $type; } + private function resolveTypeName($name, \ReflectionMethod $reflectionMethod) + { + if ('self' === $lcName = strtolower($name)) { + return $reflectionMethod->getDeclaringClass()->name; + } + if ('parent' === $lcName && $parent = $reflectionMethod->getDeclaringClass()->getParentClass()) { + return $parent->name; + } + + return $name; + } + /** * Does the class have the given public property? * diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractors/PhpDocExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php similarity index 99% rename from src/Symfony/Component/PropertyInfo/Tests/Extractors/PhpDocExtractorTest.php rename to src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php index dab463f7e5105..647d4c1072e64 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractors/PhpDocExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\PropertyInfo\Tests\PhpDocExtractors; +namespace Symfony\Component\PropertyInfo\Tests\PhpDocExtractor; use PHPUnit\Framework\TestCase; use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor; diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractors/ReflectionExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php similarity index 93% rename from src/Symfony/Component/PropertyInfo/Tests/Extractors/ReflectionExtractorTest.php rename to src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php index 176e1511f8c22..f8c61dd48aa36 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractors/ReflectionExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php @@ -52,6 +52,8 @@ public function testGetProperties() 'DOB', 'Id', '123', + 'self', + 'realParent', 'c', 'd', 'e', @@ -135,6 +137,8 @@ public function typesProvider() array('donotexist', null), array('staticGetter', null), array('staticSetter', null), + array('self', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy'))), + array('realParent', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy'))), ); } @@ -153,6 +157,8 @@ public function php7TypesProvider() array('foo', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true))), array('bar', array(new Type(Type::BUILTIN_TYPE_INT))), array('baz', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING)))), + array('buz', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\Php7Dummy'))), + array('biz', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'stdClass'))), array('donotexist', null), ); } diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractors/SerializerExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/SerializerExtractorTest.php similarity index 95% rename from src/Symfony/Component/PropertyInfo/Tests/Extractors/SerializerExtractorTest.php rename to src/Symfony/Component/PropertyInfo/Tests/Extractor/SerializerExtractorTest.php index b1be5ee11821e..91cdf80f8d608 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractors/SerializerExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/SerializerExtractorTest.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\PropertyInfo\Tests\Extractors; +namespace Symfony\Component\PropertyInfo\Tests\Extractor; use Doctrine\Common\Annotations\AnnotationReader; use PHPUnit\Framework\TestCase; diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php index 4e558eca014e5..5993d6e1d47e8 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php @@ -127,4 +127,18 @@ public function getId() public function get123() { } + + /** + * @param self $self + */ + public function setSelf(self $self) + { + } + + /** + * @param parent $realParent + */ + public function setRealParent(parent $realParent) + { + } } diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php7Dummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php7Dummy.php index cd5ba380d9d53..5dcb4c565e768 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php7Dummy.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php7Dummy.php @@ -14,7 +14,7 @@ /** * @author Kévin Dunglas */ -class Php7Dummy +class Php7Dummy extends \stdClass { public function getFoo(): array { @@ -27,4 +27,12 @@ public function setBar(int $bar) public function addBaz(string $baz) { } + + public function getBuz(): self + { + } + + public function getBiz(): parent + { + } } From 71a1e29c82a594ae7c8a774f8ea96f380e95b200 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 16 May 2018 16:41:07 +0200 Subject: [PATCH 62/76] Fix CS --- .../Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php | 4 ++-- .../DependencyInjection/Compiler/TwigEnvironmentPass.php | 1 - .../HttpKernel/Controller/ControllerResolver.php | 2 +- .../Transport/Serialization/EncoderInterface.php | 1 + .../Component/Routing/Loader/AnnotationClassLoader.php | 1 - .../Component/Routing/Matcher/Dumper/PhpMatcherDumper.php | 2 +- .../Tests/Generator/Dumper/PhpGeneratorDumperTest.php | 6 +++--- .../Routing/Tests/Loader/AnnotationClassLoaderTest.php | 8 ++++++-- .../Component/Routing/Tests/Loader/XmlFileLoaderTest.php | 1 - .../Tests/Mapping/ClassDiscriminatorMappingTest.php | 4 ++-- .../Translation/Tests/Command/XliffLintCommandTest.php | 3 --- 11 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php b/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php index a46617db081d9..4a2a3a2fd8840 100644 --- a/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php +++ b/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php @@ -60,8 +60,8 @@ public function guessType($class, $property) case Type::DATETIMETZ: case 'vardatetime': return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateTimeType', array(), Guess::HIGH_CONFIDENCE); - case 'datetime_immutable'; - case 'datetimetz_immutable'; + case 'datetime_immutable': + case 'datetimetz_immutable': return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateTimeType', array('input' => 'datetime_immutable'), Guess::HIGH_CONFIDENCE); case 'dateinterval': return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateIntervalType', array(), Guess::HIGH_CONFIDENCE); diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php index 082870249ba44..3529a6f2b1bbf 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php @@ -14,7 +14,6 @@ use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\Reference; /** * Adds tagged twig.extension services to twig service. diff --git a/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php b/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php index 274602a6ad470..dce640e31dadc 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php @@ -199,7 +199,7 @@ private function getClassMethodsWithoutMagicMethods($classOrObject) { $methods = get_class_methods($classOrObject); - return array_filter($methods, function(string $method) { + return array_filter($methods, function (string $method) { return 0 !== strncmp($method, '__', 2); }); } diff --git a/src/Symfony/Component/Messenger/Transport/Serialization/EncoderInterface.php b/src/Symfony/Component/Messenger/Transport/Serialization/EncoderInterface.php index 1dce6fdae33cd..b5ea2acd937d3 100644 --- a/src/Symfony/Component/Messenger/Transport/Serialization/EncoderInterface.php +++ b/src/Symfony/Component/Messenger/Transport/Serialization/EncoderInterface.php @@ -10,6 +10,7 @@ */ namespace Symfony\Component\Messenger\Transport\Serialization; + use Symfony\Component\Messenger\Envelope; /** diff --git a/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php b/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php index 36a41c039b9d0..c909da17bbdbe 100644 --- a/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php +++ b/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php @@ -13,7 +13,6 @@ use Doctrine\Common\Annotations\Reader; use Symfony\Component\Config\Resource\FileResource; -use Symfony\Component\Routing\Annotation\Route as RouteAnnotation; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Config\Loader\LoaderInterface; diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php index bbdb8af0076ea..0a79379160256 100644 --- a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php +++ b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php @@ -473,7 +473,7 @@ private function compileStaticPrefixCollection(StaticPrefixCollection $tree, \st $code .= $this->indent($this->compileStaticPrefixCollection($route, $state, $prefixLen + strlen($prefix))); $code .= "\n .')'"; $state->regex .= ')'; - $state->markTail += 1; + ++$state->markTail; continue; } diff --git a/src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php b/src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php index dc84e29345b60..dfcac06b40b2f 100644 --- a/src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php +++ b/src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php @@ -89,9 +89,9 @@ public function testDumpWithLocalizedRoutes() $this->routeCollection->add('test.en', (new Route('/testing/is/fun'))->setDefault('_locale', 'en')->setDefault('_canonical_route', 'test')); $this->routeCollection->add('test.nl', (new Route('/testen/is/leuk'))->setDefault('_locale', 'nl')->setDefault('_canonical_route', 'test')); - $code = $this->generatorDumper->dump([ + $code = $this->generatorDumper->dump(array( 'class' => 'LocalizedProjectUrlGenerator', - ]); + )); file_put_contents($this->testTmpFilepath, $code); include $this->testTmpFilepath; @@ -99,7 +99,7 @@ public function testDumpWithLocalizedRoutes() $projectUrlGenerator = new \LocalizedProjectUrlGenerator($context, null, 'en'); $urlWithDefaultLocale = $projectUrlGenerator->generate('test'); - $urlWithSpecifiedLocale = $projectUrlGenerator->generate('test', ['_locale' => 'nl']); + $urlWithSpecifiedLocale = $projectUrlGenerator->generate('test', array('_locale' => 'nl')); $context->setParameter('_locale', 'en'); $urlWithEnglishContext = $projectUrlGenerator->generate('test'); $context->setParameter('_locale', 'nl'); diff --git a/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php index ed517e692cf76..dd9af9db23f72 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php @@ -46,7 +46,9 @@ protected function setUp() { $reader = new AnnotationReader(); $this->loader = new class($reader) extends AnnotationClassLoader { - protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, $annot) {} + protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, $annot) + { + } }; AnnotationRegistry::registerLoader('class_exists'); } @@ -194,7 +196,9 @@ public function testInvokableClassMultipleRouteLoad() ->will($this->returnValue(array())) ; $loader = new class($reader) extends AnnotationClassLoader { - protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, $annot) {} + protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, $annot) + { + } }; $routeCollection = $loader->load('Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BazClass'); diff --git a/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php index c3d59b29be49c..fff4154b17d5e 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php @@ -420,7 +420,6 @@ public function testImportRouteWithGlobMatchingMultipleFiles() $route = $routeCollection->get('baz_route'); $this->assertSame('AppBundle:Baz:view', $route->getDefault('_controller')); - } public function testImportRouteWithNamePrefix() diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/ClassDiscriminatorMappingTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/ClassDiscriminatorMappingTest.php index 390a5b281e99d..68a092d7019f7 100644 --- a/src/Symfony/Component/Serializer/Tests/Mapping/ClassDiscriminatorMappingTest.php +++ b/src/Symfony/Component/Serializer/Tests/Mapping/ClassDiscriminatorMappingTest.php @@ -28,7 +28,7 @@ public function testGetClass() )); $this->assertEquals(AbstractDummyFirstChild::class, $mapping->getClassForType('first')); - $this->assertEquals(null, $mapping->getClassForType('second')); + $this->assertNull($mapping->getClassForType('second')); } public function testMappedObjectType() @@ -38,6 +38,6 @@ public function testMappedObjectType() )); $this->assertEquals('first', $mapping->getMappedObjectType(new AbstractDummyFirstChild())); - $this->assertEquals(null, $mapping->getMappedObjectType(new AbstractDummySecondChild())); + $this->assertNull($mapping->getMappedObjectType(new AbstractDummySecondChild())); } } diff --git a/src/Symfony/Component/Translation/Tests/Command/XliffLintCommandTest.php b/src/Symfony/Component/Translation/Tests/Command/XliffLintCommandTest.php index d55bfbc143d1f..e37600c3ebb87 100644 --- a/src/Symfony/Component/Translation/Tests/Command/XliffLintCommandTest.php +++ b/src/Symfony/Component/Translation/Tests/Command/XliffLintCommandTest.php @@ -15,9 +15,6 @@ use Symfony\Component\Console\Application; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Tester\CommandTester; -use Symfony\Component\Console\Helper\HelperSet; -use Symfony\Component\Console\Input\InputDefinition; -use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\Translation\Command\XliffLintCommand; /** From c18813d13dfcdd45a0920d6361cb8a277c2546c5 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 16 May 2018 17:16:55 +0200 Subject: [PATCH 63/76] Fix dep --- src/Symfony/Bundle/SecurityBundle/composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index 2e8c20312c9e9..1d0858c2def97 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -46,6 +46,7 @@ "twig/twig": "~1.34|~2.4" }, "conflict": { + "symfony/security": "4.1.0-beta1", "symfony/var-dumper": "<3.3", "symfony/event-dispatcher": "<3.4", "symfony/framework-bundle": "<3.4", From 68eda493229efeba8d64cb4f190e3504c7d52f2a Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 16 May 2018 18:27:27 +0200 Subject: [PATCH 64/76] [DI] Fix hidding service in autowiring errors --- .../Component/DependencyInjection/Compiler/AutowirePass.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index f94284d2e0722..0fece9802e205 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -308,7 +308,7 @@ private function populateAvailableType(string $id, Definition $definition) return; } - if (preg_match('/^\d+_[^~]++~[._a-zA-Z\d]{7}$/', $id) || $definition->isDeprecated() || !$reflectionClass = $this->container->getReflectionClass($definition->getClass(), false)) { + if ('' === $id || '.' === $id[0] || $definition->isDeprecated() || !$reflectionClass = $this->container->getReflectionClass($definition->getClass(), false)) { return; } From 44cef5a69d281c79338a87b9db157c0738cd391e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 21 Nov 2017 17:18:34 +0100 Subject: [PATCH 65/76] Fix security/* cross-dependencies --- src/Symfony/Bridge/Doctrine/composer.json | 2 +- src/Symfony/Bridge/Twig/composer.json | 2 +- src/Symfony/Bundle/FrameworkBundle/composer.json | 3 +-- src/Symfony/Component/Form/composer.json | 2 +- src/Symfony/Component/Security/Guard/composer.json | 2 +- src/Symfony/Component/Security/Http/composer.json | 2 +- 6 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/composer.json b/src/Symfony/Bridge/Doctrine/composer.json index 20d02abac37f8..e5b5a39a38269 100644 --- a/src/Symfony/Bridge/Doctrine/composer.json +++ b/src/Symfony/Bridge/Doctrine/composer.json @@ -28,7 +28,7 @@ "symfony/http-kernel": "~2.2|~3.0.0", "symfony/property-access": "~2.3|~3.0.0", "symfony/property-info": "~2.8|3.0", - "symfony/security": "~2.2|~3.0.0", + "symfony/security": "^2.8.31|^3.3.13", "symfony/expression-language": "~2.2|~3.0.0", "symfony/validator": "~2.7.25|^2.8.18|~3.2.5", "symfony/translation": "^2.0.5|~3.0.0", diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index e1937deb671ea..48af8a77b4033 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -30,7 +30,7 @@ "symfony/templating": "~2.1|~3.0.0", "symfony/translation": "~2.7|~3.0.0", "symfony/yaml": "^2.0.5|~3.0.0", - "symfony/security": "~2.6|~3.0.0", + "symfony/security": "^2.8.31|^3.3.13", "symfony/security-acl": "~2.6|~3.0.0", "symfony/stopwatch": "~2.2|~3.0.0", "symfony/console": "~2.8|~3.0.0", diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 14de4ad38afbf..3e7b75ba127e2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -30,7 +30,7 @@ "symfony/filesystem": "~2.3|~3.0.0", "symfony/routing": "^2.8.17", "symfony/security-core": "~2.6.13|~2.7.9|~2.8|~3.0.0", - "symfony/security-csrf": "~2.6|~3.0.0", + "symfony/security-csrf": "^2.8.31|^3.3.13", "symfony/stopwatch": "~2.3|~3.0.0", "symfony/templating": "~2.7|~3.0.0", "symfony/translation": "~2.8", @@ -43,7 +43,6 @@ "symfony/css-selector": "^2.0.5|~3.0.0", "symfony/dom-crawler": "^2.0.5|~3.0.0", "symfony/polyfill-intl-icu": "~1.0", - "symfony/security": "~2.6|~3.0.0", "symfony/form": "^2.8.19", "symfony/expression-language": "~2.6|~3.0.0", "symfony/process": "^2.0.5|~3.0.0", diff --git a/src/Symfony/Component/Form/composer.json b/src/Symfony/Component/Form/composer.json index d670186cb7dc4..90cc9523fb70a 100644 --- a/src/Symfony/Component/Form/composer.json +++ b/src/Symfony/Component/Form/composer.json @@ -30,7 +30,7 @@ "symfony/dependency-injection": "~2.7|~3.0.0", "symfony/http-foundation": "~2.2|~3.0.0", "symfony/http-kernel": "~2.4|~3.0.0", - "symfony/security-csrf": "~2.4|~3.0.0", + "symfony/security-csrf": "^2.8.31|^3.3.13", "symfony/translation": "^2.0.5|~3.0.0" }, "conflict": { diff --git a/src/Symfony/Component/Security/Guard/composer.json b/src/Symfony/Component/Security/Guard/composer.json index 320892095cb1d..35c7456638ea8 100644 --- a/src/Symfony/Component/Security/Guard/composer.json +++ b/src/Symfony/Component/Security/Guard/composer.json @@ -18,7 +18,7 @@ "require": { "php": ">=5.3.9", "symfony/security-core": "~2.8|~3.0.0", - "symfony/security-http": "~2.7|~3.0.0" + "symfony/security-http": "^2.8.31|^3.3.13" }, "require-dev": { "psr/log": "~1.0" diff --git a/src/Symfony/Component/Security/Http/composer.json b/src/Symfony/Component/Security/Http/composer.json index bde88858ae5a3..a604766c0002f 100644 --- a/src/Symfony/Component/Security/Http/composer.json +++ b/src/Symfony/Component/Security/Http/composer.json @@ -27,7 +27,7 @@ }, "require-dev": { "symfony/routing": "~2.2|~3.0.0", - "symfony/security-csrf": "~2.4|~3.0.0", + "symfony/security-csrf": "^2.8.31|^3.3.13", "psr/log": "~1.0" }, "suggest": { From 0de3a61cfcf122afa0ce8015c837c8f6d8ebc33e Mon Sep 17 00:00:00 2001 From: Kyle Date: Wed, 16 May 2018 15:58:59 +0200 Subject: [PATCH 66/76] Add Occitan plural rule --- src/Symfony/Component/Translation/PluralizationRules.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Symfony/Component/Translation/PluralizationRules.php b/src/Symfony/Component/Translation/PluralizationRules.php index e5ece89620b7f..2b7b118336938 100644 --- a/src/Symfony/Component/Translation/PluralizationRules.php +++ b/src/Symfony/Component/Translation/PluralizationRules.php @@ -107,6 +107,7 @@ public static function get($number, $locale) case 'nl': case 'nn': case 'no': + case 'oc': case 'om': case 'or': case 'pa': From 4c3b950dc274e356950070da9cfb78f58d317aa4 Mon Sep 17 00:00:00 2001 From: "Thomason, James" Date: Mon, 14 May 2018 13:29:05 -0400 Subject: [PATCH 67/76] [DependencyInjection] resolve array env vars --- .../DependencyInjection/ContainerBuilder.php | 8 ++- .../Tests/ContainerBuilderTest.php | 52 ++++++++++++++----- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 7f79e2f4ffc55..7296a098ee0ed 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -1408,6 +1408,7 @@ public function resolveEnvPlaceholders($value, $format = null, array &$usedEnvs } $envPlaceholders = $bag instanceof EnvPlaceholderParameterBag ? $bag->getEnvPlaceholders() : $this->envPlaceholders; + $completed = false; foreach ($envPlaceholders as $env => $placeholders) { foreach ($placeholders as $placeholder) { if (false !== stripos($value, $placeholder)) { @@ -1418,14 +1419,19 @@ public function resolveEnvPlaceholders($value, $format = null, array &$usedEnvs } if ($placeholder === $value) { $value = $resolved; + $completed = true; } else { if (!is_string($resolved) && !is_numeric($resolved)) { - throw new RuntimeException(sprintf('A string value must be composed of strings and/or numbers, but found parameter "env(%s)" of type %s inside string value "%s".', $env, gettype($resolved), $value)); + throw new RuntimeException(sprintf('A string value must be composed of strings and/or numbers, but found parameter "env(%s)" of type %s inside string value "%s".', $env, gettype($resolved), $this->resolveEnvPlaceholders($value))); } $value = str_ireplace($placeholder, $resolved, $value); } $usedEnvs[$env] = $env; $this->envCounters[$env] = isset($this->envCounters[$env]) ? 1 + $this->envCounters[$env] : 1; + + if ($completed) { + break 2; + } } } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 51038b6b1550f..f2e8368455f66 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -665,17 +665,49 @@ public function testCompileWithResolveEnv() putenv('DUMMY_ENV_VAR'); } + public function testCompileWithArrayResolveEnv() + { + putenv('ARRAY={"foo":"bar"}'); + + $container = new ContainerBuilder(); + $container->setParameter('foo', '%env(json:ARRAY)%'); + $container->compile(true); + + $this->assertSame(array('foo' => 'bar'), $container->getParameter('foo')); + + putenv('ARRAY'); + } + + public function testCompileWithArrayAndAnotherResolveEnv() + { + putenv('DUMMY_ENV_VAR=abc'); + putenv('ARRAY={"foo":"bar"}'); + + $container = new ContainerBuilder(); + $container->setParameter('foo', '%env(json:ARRAY)%'); + $container->setParameter('bar', '%env(DUMMY_ENV_VAR)%'); + $container->compile(true); + + $this->assertSame(array('foo' => 'bar'), $container->getParameter('foo')); + $this->assertSame('abc', $container->getParameter('bar')); + + putenv('DUMMY_ENV_VAR'); + putenv('ARRAY'); + } + /** * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage A string value must be composed of strings and/or numbers, but found parameter "env(ARRAY)" of type array inside string value "ABC %env(ARRAY)%". + * @expectedExceptionMessage A string value must be composed of strings and/or numbers, but found parameter "env(json:ARRAY)" of type array inside string value "ABC %env(json:ARRAY)%". */ - public function testCompileWithArrayResolveEnv() + public function testCompileWithArrayInStringResolveEnv() { - $bag = new TestingEnvPlaceholderParameterBag(); - $container = new ContainerBuilder($bag); - $container->setParameter('foo', '%env(ARRAY)%'); - $container->setParameter('bar', 'ABC %env(ARRAY)%'); + putenv('ARRAY={"foo":"bar"}'); + + $container = new ContainerBuilder(); + $container->setParameter('foo', 'ABC %env(json:ARRAY)%'); $container->compile(true); + + putenv('ARRAY'); } /** @@ -1418,11 +1450,3 @@ public function __construct(A $a) { } } - -class TestingEnvPlaceholderParameterBag extends EnvPlaceholderParameterBag -{ - public function get($name) - { - return 'env(array)' === strtolower($name) ? array(123) : parent::get($name); - } -} From 919f93d91c73a8d95ccd991d66c9fc4ec5e7f5f2 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 18 May 2018 20:00:42 +0200 Subject: [PATCH 68/76] do not mock the session in token storage tests --- .../TokenStorage/SessionTokenStorageTest.php | 177 +++--------------- 1 file changed, 25 insertions(+), 152 deletions(-) diff --git a/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php b/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php index c629ca15255ff..306e19ad91bb9 100644 --- a/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php +++ b/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Security\Csrf\Tests\TokenStorage; use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; use Symfony\Component\Security\Csrf\TokenStorage\SessionTokenStorage; /** @@ -22,7 +24,7 @@ class SessionTokenStorageTest extends TestCase const SESSION_NAMESPACE = 'foobar'; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Session */ private $session; @@ -33,118 +35,53 @@ class SessionTokenStorageTest extends TestCase protected function setUp() { - $this->session = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\SessionInterface') - ->disableOriginalConstructor() - ->getMock(); + $this->session = new Session(new MockArraySessionStorage()); $this->storage = new SessionTokenStorage($this->session, self::SESSION_NAMESPACE); } - public function testStoreTokenInClosedSession() + public function testStoreTokenInNotStartedSessionStartsTheSession() { - $this->session->expects($this->any()) - ->method('isStarted') - ->will($this->returnValue(false)); - - $this->session->expects($this->once()) - ->method('start'); - - $this->session->expects($this->once()) - ->method('set') - ->with(self::SESSION_NAMESPACE.'/token_id', 'TOKEN'); - $this->storage->setToken('token_id', 'TOKEN'); + + $this->assertTrue($this->session->isStarted()); } public function testStoreTokenInActiveSession() { - $this->session->expects($this->any()) - ->method('isStarted') - ->will($this->returnValue(true)); - - $this->session->expects($this->never()) - ->method('start'); - - $this->session->expects($this->once()) - ->method('set') - ->with(self::SESSION_NAMESPACE.'/token_id', 'TOKEN'); - + $this->session->start(); $this->storage->setToken('token_id', 'TOKEN'); + + $this->assertSame('TOKEN', $this->session->get(self::SESSION_NAMESPACE.'/token_id')); } public function testCheckTokenInClosedSession() { - $this->session->expects($this->any()) - ->method('isStarted') - ->will($this->returnValue(false)); - - $this->session->expects($this->once()) - ->method('start'); + $this->session->set(self::SESSION_NAMESPACE.'/token_id', 'RESULT'); - $this->session->expects($this->once()) - ->method('has') - ->with(self::SESSION_NAMESPACE.'/token_id') - ->will($this->returnValue('RESULT')); - - $this->assertSame('RESULT', $this->storage->hasToken('token_id')); + $this->assertTrue($this->storage->hasToken('token_id')); + $this->assertTrue($this->session->isStarted()); } public function testCheckTokenInActiveSession() { - $this->session->expects($this->any()) - ->method('isStarted') - ->will($this->returnValue(true)); - - $this->session->expects($this->never()) - ->method('start'); + $this->session->start(); + $this->session->set(self::SESSION_NAMESPACE.'/token_id', 'RESULT'); - $this->session->expects($this->once()) - ->method('has') - ->with(self::SESSION_NAMESPACE.'/token_id') - ->will($this->returnValue('RESULT')); - - $this->assertSame('RESULT', $this->storage->hasToken('token_id')); + $this->assertTrue($this->storage->hasToken('token_id')); } public function testGetExistingTokenFromClosedSession() { - $this->session->expects($this->any()) - ->method('isStarted') - ->will($this->returnValue(false)); - - $this->session->expects($this->once()) - ->method('start'); - - $this->session->expects($this->once()) - ->method('has') - ->with(self::SESSION_NAMESPACE.'/token_id') - ->will($this->returnValue(true)); - - $this->session->expects($this->once()) - ->method('get') - ->with(self::SESSION_NAMESPACE.'/token_id') - ->will($this->returnValue('RESULT')); + $this->session->set(self::SESSION_NAMESPACE.'/token_id', 'RESULT'); $this->assertSame('RESULT', $this->storage->getToken('token_id')); + $this->assertTrue($this->session->isStarted()); } public function testGetExistingTokenFromActiveSession() { - $this->session->expects($this->any()) - ->method('isStarted') - ->will($this->returnValue(true)); - - $this->session->expects($this->never()) - ->method('start'); - - $this->session->expects($this->once()) - ->method('has') - ->with(self::SESSION_NAMESPACE.'/token_id') - ->will($this->returnValue(true)); - - $this->session->expects($this->once()) - ->method('get') - ->with(self::SESSION_NAMESPACE.'/token_id') - ->will($this->returnValue('RESULT')); + $this->session->start(); + $this->session->set(self::SESSION_NAMESPACE.'/token_id', 'RESULT'); $this->assertSame('RESULT', $this->storage->getToken('token_id')); } @@ -154,18 +91,6 @@ public function testGetExistingTokenFromActiveSession() */ public function testGetNonExistingTokenFromClosedSession() { - $this->session->expects($this->any()) - ->method('isStarted') - ->will($this->returnValue(false)); - - $this->session->expects($this->once()) - ->method('start'); - - $this->session->expects($this->once()) - ->method('has') - ->with(self::SESSION_NAMESPACE.'/token_id') - ->will($this->returnValue(false)); - $this->storage->getToken('token_id'); } @@ -174,85 +99,33 @@ public function testGetNonExistingTokenFromClosedSession() */ public function testGetNonExistingTokenFromActiveSession() { - $this->session->expects($this->any()) - ->method('isStarted') - ->will($this->returnValue(true)); - - $this->session->expects($this->never()) - ->method('start'); - - $this->session->expects($this->once()) - ->method('has') - ->with(self::SESSION_NAMESPACE.'/token_id') - ->will($this->returnValue(false)); - + $this->session->start(); $this->storage->getToken('token_id'); } public function testRemoveNonExistingTokenFromClosedSession() { - $this->session->expects($this->any()) - ->method('isStarted') - ->will($this->returnValue(false)); - - $this->session->expects($this->once()) - ->method('start'); - - $this->session->expects($this->once()) - ->method('remove') - ->with(self::SESSION_NAMESPACE.'/token_id') - ->will($this->returnValue(null)); - $this->assertNull($this->storage->removeToken('token_id')); } public function testRemoveNonExistingTokenFromActiveSession() { - $this->session->expects($this->any()) - ->method('isStarted') - ->will($this->returnValue(true)); - - $this->session->expects($this->never()) - ->method('start'); - - $this->session->expects($this->once()) - ->method('remove') - ->with(self::SESSION_NAMESPACE.'/token_id') - ->will($this->returnValue(null)); + $this->session->start(); $this->assertNull($this->storage->removeToken('token_id')); } public function testRemoveExistingTokenFromClosedSession() { - $this->session->expects($this->any()) - ->method('isStarted') - ->will($this->returnValue(false)); - - $this->session->expects($this->once()) - ->method('start'); - - $this->session->expects($this->once()) - ->method('remove') - ->with(self::SESSION_NAMESPACE.'/token_id') - ->will($this->returnValue('TOKEN')); + $this->session->set(self::SESSION_NAMESPACE.'/token_id', 'TOKEN'); $this->assertSame('TOKEN', $this->storage->removeToken('token_id')); } public function testRemoveExistingTokenFromActiveSession() { - $this->session->expects($this->any()) - ->method('isStarted') - ->will($this->returnValue(true)); - - $this->session->expects($this->never()) - ->method('start'); - - $this->session->expects($this->once()) - ->method('remove') - ->with(self::SESSION_NAMESPACE.'/token_id') - ->will($this->returnValue('TOKEN')); + $this->session->start(); + $this->session->set(self::SESSION_NAMESPACE.'/token_id', 'TOKEN'); $this->assertSame('TOKEN', $this->storage->removeToken('token_id')); } From 9d658e915e982d9f76f833cdad3c68616332ca4a Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Tue, 15 May 2018 16:03:44 +0200 Subject: [PATCH 69/76] [Messenger] Allow to scope handlers per bus --- .../FrameworkExtension.php | 2 +- .../Resources/config/messenger.xml | 7 +- .../Messenger/Command/DebugCommand.php | 51 ++++-- .../DependencyInjection/MessengerPass.php | 75 ++++++--- .../Tests/Command/DebugCommandTest.php | 155 ++++++++++++++++++ .../DependencyInjection/MessengerPassTest.php | 154 ++++++++++++++--- .../Messenger/Tests/Fixtures/DummyCommand.php | 16 ++ .../Tests/Fixtures/DummyCommandHandler.php | 19 +++ .../Messenger/Tests/Fixtures/DummyQuery.php | 16 ++ .../Tests/Fixtures/DummyQueryHandler.php | 19 +++ .../Tests/Fixtures/MultipleBusesMessage.php | 16 ++ .../Fixtures/MultipleBusesMessageHandler.php | 19 +++ 12 files changed, 478 insertions(+), 71 deletions(-) create mode 100644 src/Symfony/Component/Messenger/Tests/Command/DebugCommandTest.php create mode 100644 src/Symfony/Component/Messenger/Tests/Fixtures/DummyCommand.php create mode 100644 src/Symfony/Component/Messenger/Tests/Fixtures/DummyCommandHandler.php create mode 100644 src/Symfony/Component/Messenger/Tests/Fixtures/DummyQuery.php create mode 100644 src/Symfony/Component/Messenger/Tests/Fixtures/DummyQueryHandler.php create mode 100644 src/Symfony/Component/Messenger/Tests/Fixtures/MultipleBusesMessage.php create mode 100644 src/Symfony/Component/Messenger/Tests/Fixtures/MultipleBusesMessageHandler.php diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index e8dfbd14a1695..318af6c8b9a2f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -1483,7 +1483,7 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder } $container->setParameter($busId.'.middleware', $middleware); - $container->setDefinition($busId, (new Definition(MessageBus::class, array(array())))->addTag('messenger.bus')); + $container->register($busId, MessageBus::class)->addArgument(array())->addTag('messenger.bus'); if ($busId === $config['default_bus']) { $container->setAlias('message_bus', $busId); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml index 32a4a4d895351..0b09978d33870 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml @@ -7,11 +7,6 @@ - - - - - @@ -32,7 +27,7 @@ - + diff --git a/src/Symfony/Component/Messenger/Command/DebugCommand.php b/src/Symfony/Component/Messenger/Command/DebugCommand.php index f41747dc0f649..38d4e4253cec2 100644 --- a/src/Symfony/Component/Messenger/Command/DebugCommand.php +++ b/src/Symfony/Component/Messenger/Command/DebugCommand.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Messenger\Command; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; @@ -31,9 +33,9 @@ class DebugCommand extends Command public function __construct(array $mapping) { - parent::__construct(); - $this->mapping = $mapping; + + parent::__construct(); } /** @@ -42,13 +44,18 @@ public function __construct(array $mapping) protected function configure() { $this - ->setDescription('Lists messages you can dispatch using the message bus') + ->addArgument('bus', InputArgument::OPTIONAL, sprintf('The bus id (one of %s)', implode(', ', array_keys($this->mapping))), null) + ->setDescription('Lists messages you can dispatch using the message buses') ->setHelp(<<<'EOF' The %command.name% command displays all messages that can be -dispatched using the message bus: +dispatched using the message buses: php %command.full_name% +Or for a specific bus only: + + php %command.full_name% command_bus + EOF ) ; @@ -61,21 +68,33 @@ protected function execute(InputInterface $input, OutputInterface $output) { $io = new SymfonyStyle($input, $output); $io->title('Messenger'); - $io->text('The following messages can be dispatched:'); - $io->newLine(); - - $tableRows = array(); - foreach ($this->mapping as $message => $handlers) { - $tableRows[] = array(sprintf('%s', $message)); - foreach ($handlers as $handler) { - $tableRows[] = array(sprintf(' handled by %s', $handler)); + + $mapping = $this->mapping; + if ($bus = $input->getArgument('bus')) { + if (!isset($mapping[$bus])) { + throw new RuntimeException(sprintf('Bus "%s" does not exist. Known buses are %s.', $bus, implode(', ', array_keys($this->mapping)))); } + $mapping = array($bus => $mapping[$bus]); } - if ($tableRows) { - $io->table(array(), $tableRows); - } else { - $io->text('No messages were found that have valid handlers.'); + foreach ($mapping as $bus => $handlersByMessage) { + $io->section($bus); + + $tableRows = array(); + foreach ($handlersByMessage as $message => $handlers) { + $tableRows[] = array(sprintf('%s', $message)); + foreach ($handlers as $handler) { + $tableRows[] = array(sprintf(' handled by %s', $handler)); + } + } + + if ($tableRows) { + $io->text('The following messages can be dispatched:'); + $io->newLine(); + $io->table(array(), $tableRows); + } else { + $io->warning(sprintf('No handled message found in bus "%s".', $bus)); + } } } } diff --git a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php index f2767058c6f01..7836637dde86c 100644 --- a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php +++ b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php @@ -20,6 +20,7 @@ use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\Messenger\Handler\ChainHandler; +use Symfony\Component\Messenger\Handler\Locator\ContainerHandlerLocator; use Symfony\Component\Messenger\Handler\MessageHandlerInterface; use Symfony\Component\Messenger\Handler\MessageSubscriberInterface; use Symfony\Component\Messenger\TraceableMessageBus; @@ -51,11 +52,13 @@ public function __construct(string $handlerTag = 'messenger.message_handler', st */ public function process(ContainerBuilder $container) { - if (!$container->hasDefinition('messenger.handler_resolver')) { + if (!$container->has('message_bus')) { return; } + $busIds = array(); foreach ($container->findTaggedServiceIds($this->busTag) as $busId => $tags) { + $busIds[] = $busId; if ($container->hasParameter($busMiddlewareParameter = $busId.'.middleware')) { $this->registerBusMiddleware($container, $busId, $container->getParameter($busMiddlewareParameter)); @@ -69,16 +72,20 @@ public function process(ContainerBuilder $container) $this->registerReceivers($container); $this->registerSenders($container); - $this->registerHandlers($container); + $this->registerHandlers($container, $busIds); } - private function registerHandlers(ContainerBuilder $container) + private function registerHandlers(ContainerBuilder $container, array $busIds) { $definitions = array(); - $handlersByMessage = array(); + $handlersByBusAndMessage = array(); foreach ($container->findTaggedServiceIds($this->handlerTag, true) as $serviceId => $tags) { foreach ($tags as $tag) { + if (isset($tag['bus']) && !\in_array($tag['bus'], $busIds, true)) { + throw new RuntimeException(sprintf('Invalid handler service "%s": bus "%s" specified on the tag "%s" does not exist (known ones are: %s).', $serviceId, $tag['bus'], $this->handlerTag, implode(', ', $busIds))); + } + $r = $container->getReflectionClass($container->getDefinition($serviceId)->getClass()); if (isset($tag['handles'])) { @@ -88,6 +95,7 @@ private function registerHandlers(ContainerBuilder $container) } $priority = $tag['priority'] ?? 0; + $handlerBuses = (array) ($tag['bus'] ?? $busIds); foreach ($handles as $messageClass => $method) { if (\is_int($messageClass)) { @@ -123,38 +131,57 @@ private function registerHandlers(ContainerBuilder $container) $definitions[$serviceId = '.messenger.method_on_object_wrapper.'.ContainerBuilder::hash($messageClass.':'.$messagePriority.':'.$serviceId.':'.$method)] = $wrapperDefinition; } - $handlersByMessage[$messageClass][$messagePriority][] = new Reference($serviceId); + foreach ($handlerBuses as $handlerBus) { + $handlersByBusAndMessage[$handlerBus][$messageClass][$messagePriority][] = $serviceId; + } } } } - foreach ($handlersByMessage as $message => $handlers) { - krsort($handlersByMessage[$message]); - $handlersByMessage[$message] = array_merge(...$handlersByMessage[$message]); + foreach ($handlersByBusAndMessage as $bus => $handlersByMessage) { + foreach ($handlersByMessage as $message => $handlersByPriority) { + krsort($handlersByPriority); + $handlersByBusAndMessage[$bus][$message] = array_unique(array_merge(...$handlersByPriority)); + } } - $handlersLocatorMapping = array(); - foreach ($handlersByMessage as $message => $handlers) { - if (1 === \count($handlers)) { - $handlersLocatorMapping['handler.'.$message] = current($handlers); - } else { - $d = new Definition(ChainHandler::class, array($handlers)); - $d->setPrivate(true); - $serviceId = hash('sha1', $message); - $definitions[$serviceId] = $d; - $handlersLocatorMapping['handler.'.$message] = new Reference($serviceId); + $handlersLocatorMappingByBus = array(); + foreach ($handlersByBusAndMessage as $bus => $handlersByMessage) { + foreach ($handlersByMessage as $message => $handlersIds) { + if (1 === \count($handlersIds)) { + $handlersLocatorMappingByBus[$bus]['handler.'.$message] = new Reference(current($handlersIds)); + } else { + $chainHandler = new Definition(ChainHandler::class, array(array_map(function (string $handlerId): Reference { + return new Reference($handlerId); + }, $handlersIds))); + $chainHandler->setPrivate(true); + $serviceId = '.messenger.chain_handler.'.ContainerBuilder::hash($bus.$message); + $definitions[$serviceId] = $chainHandler; + $handlersLocatorMappingByBus[$bus]['handler.'.$message] = new Reference($serviceId); + } } } $container->addDefinitions($definitions); - $handlerResolver = $container->getDefinition('messenger.handler_resolver'); - $handlerResolver->replaceArgument(0, ServiceLocatorTagPass::register($container, $handlersLocatorMapping)); + foreach ($busIds as $bus) { + $container->register($resolverName = "$bus.messenger.handler_resolver", ContainerHandlerLocator::class) + ->setArgument(0, ServiceLocatorTagPass::register($container, $handlersLocatorMappingByBus[$bus] ?? array())) + ; + if ($container->has($callMessageHandlerId = "$bus.middleware.call_message_handler")) { + $container->getDefinition($callMessageHandlerId) + ->replaceArgument(0, new Reference($resolverName)) + ; + } + } if ($container->hasDefinition('console.command.messenger_debug')) { - $container->getDefinition('console.command.messenger_debug') - ->replaceArgument(0, array_map(function (array $handlers): array { - return array_map('strval', $handlers); - }, $handlersByMessage)); + $debugCommandMapping = $handlersByBusAndMessage; + foreach ($busIds as $bus) { + if (!isset($debugCommandMapping[$bus])) { + $debugCommandMapping[$bus] = array(); + } + } + $container->getDefinition('console.command.messenger_debug')->replaceArgument(0, $debugCommandMapping); } } diff --git a/src/Symfony/Component/Messenger/Tests/Command/DebugCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/DebugCommandTest.php new file mode 100644 index 0000000000000..a707f6a99dca5 --- /dev/null +++ b/src/Symfony/Component/Messenger/Tests/Command/DebugCommandTest.php @@ -0,0 +1,155 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Tests\Command; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\Messenger\Command\DebugCommand; +use Symfony\Component\Messenger\Tests\Fixtures\DummyCommand; +use Symfony\Component\Messenger\Tests\Fixtures\DummyCommandHandler; +use Symfony\Component\Messenger\Tests\Fixtures\DummyQuery; +use Symfony\Component\Messenger\Tests\Fixtures\DummyQueryHandler; +use Symfony\Component\Messenger\Tests\Fixtures\MultipleBusesMessage; +use Symfony\Component\Messenger\Tests\Fixtures\MultipleBusesMessageHandler; + +/** + * @author Maxime Steinhausser + */ +class DebugCommandTest extends TestCase +{ + public function setUp() + { + putenv('COLUMNS='.(119 + strlen(PHP_EOL))); + } + + public function tearDown() + { + putenv('COLUMNS='); + } + + public function testOutput() + { + $command = new DebugCommand( + array( + 'command_bus' => array( + DummyCommand::class => array(DummyCommandHandler::class), + MultipleBusesMessage::class => array(MultipleBusesMessageHandler::class), + ), + 'query_bus' => array( + DummyQuery::class => array(DummyQueryHandler::class), + MultipleBusesMessage::class => array(MultipleBusesMessageHandler::class), + ), + ) + ); + + $tester = new CommandTester($command); + $tester->execute(array(), array('decorated' => false)); + + $this->assertSame(<<getDisplay(true) + ); + + $tester->execute(array('bus' => 'query_bus'), array('decorated' => false)); + + $this->assertSame(<<getDisplay(true) + ); + } + + public function testOutputWithoutMessages() + { + $command = new DebugCommand(array('command_bus' => array(), 'query_bus' => array())); + + $tester = new CommandTester($command); + $tester->execute(array(), array('decorated' => false)); + + $this->assertSame(<<getDisplay(true) + ); + } + + /** + * @expectedException \Symfony\Component\Console\Exception\RuntimeException + * @expectedExceptionMessage Bus "unknown_bus" does not exist. Known buses are command_bus, query_bus. + */ + public function testExceptionOnUnknownBusArgument() + { + $command = new DebugCommand(array('command_bus' => array(), 'query_bus' => array())); + + $tester = new CommandTester($command); + $tester->execute(array('bus' => 'unknown_bus'), array('decorated' => false)); + } +} diff --git a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php index 1b86ffa6295b5..6c77704adb90f 100644 --- a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php +++ b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php @@ -14,30 +14,38 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\Compiler\ResolveChildDefinitionsPass; +use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ServiceLocator; use Symfony\Component\Messenger\Command\ConsumeMessagesCommand; -use Symfony\Component\Messenger\Envelope; -use Symfony\Component\Messenger\Transport\AmqpExt\AmqpReceiver; -use Symfony\Component\Messenger\Transport\AmqpExt\AmqpSender; -use Symfony\Component\Messenger\Handler\Locator\ContainerHandlerLocator; +use Symfony\Component\Messenger\Command\DebugCommand; use Symfony\Component\Messenger\DataCollector\MessengerDataCollector; use Symfony\Component\Messenger\DependencyInjection\MessengerPass; +use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Handler\ChainHandler; use Symfony\Component\Messenger\Handler\MessageSubscriberInterface; use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Messenger\Middleware\AllowNoHandlerMiddleware; +use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware; use Symfony\Component\Messenger\Middleware\MiddlewareInterface; +use Symfony\Component\Messenger\Tests\Fixtures\DummyCommand; +use Symfony\Component\Messenger\Tests\Fixtures\DummyCommandHandler; use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; +use Symfony\Component\Messenger\Tests\Fixtures\DummyQuery; +use Symfony\Component\Messenger\Tests\Fixtures\DummyQueryHandler; +use Symfony\Component\Messenger\Tests\Fixtures\MultipleBusesMessage; +use Symfony\Component\Messenger\Tests\Fixtures\MultipleBusesMessageHandler; use Symfony\Component\Messenger\Tests\Fixtures\SecondMessage; +use Symfony\Component\Messenger\Transport\AmqpExt\AmqpReceiver; +use Symfony\Component\Messenger\Transport\AmqpExt\AmqpSender; use Symfony\Component\Messenger\Transport\ReceiverInterface; class MessengerPassTest extends TestCase { public function testProcess() { - $container = $this->getContainerBuilder(); + $container = $this->getContainerBuilder($busId = 'message_bus'); $container ->register(DummyHandler::class, DummyHandler::class) ->addTag('messenger.message_handler') @@ -55,7 +63,7 @@ public function testProcess() $this->assertFalse($container->hasDefinition('messenger.middleware.debug.logging')); - $handlerLocatorDefinition = $container->getDefinition($container->getDefinition('messenger.handler_resolver')->getArgument(0)); + $handlerLocatorDefinition = $container->getDefinition($container->getDefinition("$busId.messenger.handler_resolver")->getArgument(0)); $this->assertSame(ServiceLocator::class, $handlerLocatorDefinition->getClass()); $this->assertEquals( array( @@ -71,9 +79,68 @@ public function testProcess() ); } + public function testProcessHandlersByBus() + { + $container = $this->getContainerBuilder($commandBusId = 'command_bus'); + $container->register($queryBusId = 'query_bus', MessageBusInterface::class)->setArgument(0, array())->addTag('messenger.bus'); + $container->register('messenger.middleware.call_message_handler', HandleMessageMiddleware::class) + ->addArgument(null) + ->setAbstract(true) + ; + + $middlewares = array(array('id' => 'call_message_handler')); + + $container->setParameter($commandBusId.'.middleware', $middlewares); + $container->setParameter($queryBusId.'.middleware', $middlewares); + + $container->register(DummyCommandHandler::class)->addTag('messenger.message_handler', array('bus' => $commandBusId)); + $container->register(DummyQueryHandler::class)->addTag('messenger.message_handler', array('bus' => $queryBusId)); + $container->register(MultipleBusesMessageHandler::class) + ->addTag('messenger.message_handler', array('bus' => $commandBusId)) + ->addTag('messenger.message_handler', array('bus' => $queryBusId)) + ; + + (new ResolveClassPass())->process($container); + (new MessengerPass())->process($container); + + $commandBusHandlerLocatorDefinition = $container->getDefinition($container->getDefinition("$commandBusId.messenger.handler_resolver")->getArgument(0)); + $this->assertSame(ServiceLocator::class, $commandBusHandlerLocatorDefinition->getClass()); + $this->assertEquals( + array( + 'handler.'.DummyCommand::class => new ServiceClosureArgument(new Reference(DummyCommandHandler::class)), + 'handler.'.MultipleBusesMessage::class => new ServiceClosureArgument(new Reference(MultipleBusesMessageHandler::class)), + ), + $commandBusHandlerLocatorDefinition->getArgument(0) + ); + + $queryBusHandlerLocatorDefinition = $container->getDefinition($container->getDefinition("$queryBusId.messenger.handler_resolver")->getArgument(0)); + $this->assertSame(ServiceLocator::class, $queryBusHandlerLocatorDefinition->getClass()); + $this->assertEquals( + array( + 'handler.'.DummyQuery::class => new ServiceClosureArgument(new Reference(DummyQueryHandler::class)), + 'handler.'.MultipleBusesMessage::class => new ServiceClosureArgument(new Reference(MultipleBusesMessageHandler::class)), + ), + $queryBusHandlerLocatorDefinition->getArgument(0) + ); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Invalid handler service "Symfony\Component\Messenger\Tests\Fixtures\DummyCommandHandler": bus "unknown_bus" specified on the tag "messenger.message_handler" does not exist (known ones are: command_bus). + */ + public function testProcessTagWithUnknownBus() + { + $container = $this->getContainerBuilder($commandBusId = 'command_bus'); + + $container->register(DummyCommandHandler::class)->addTag('messenger.message_handler', array('bus' => 'unknown_bus')); + + (new ResolveClassPass())->process($container); + (new MessengerPass())->process($container); + } + public function testGetClassesFromTheHandlerSubscriberInterface() { - $container = $this->getContainerBuilder(); + $container = $this->getContainerBuilder($busId = 'message_bus'); $container ->register(HandlerWithMultipleMessages::class, HandlerWithMultipleMessages::class) ->addTag('messenger.message_handler') @@ -85,7 +152,7 @@ public function testGetClassesFromTheHandlerSubscriberInterface() (new MessengerPass())->process($container); - $handlerLocatorDefinition = $container->getDefinition($container->getDefinition('messenger.handler_resolver')->getArgument(0)); + $handlerLocatorDefinition = $container->getDefinition($container->getDefinition("$busId.messenger.handler_resolver")->getArgument(0)); $handlerMapping = $handlerLocatorDefinition->getArgument(0); $this->assertArrayHasKey('handler.'.DummyMessage::class, $handlerMapping); @@ -101,7 +168,7 @@ public function testGetClassesFromTheHandlerSubscriberInterface() public function testGetClassesAndMethodsAndPrioritiesFromTheSubscriber() { - $container = $this->getContainerBuilder(); + $container = $this->getContainerBuilder($busId = 'message_bus'); $container ->register(HandlerMappingMethods::class, HandlerMappingMethods::class) ->addTag('messenger.message_handler') @@ -113,7 +180,7 @@ public function testGetClassesAndMethodsAndPrioritiesFromTheSubscriber() (new MessengerPass())->process($container); - $handlerLocatorDefinition = $container->getDefinition($container->getDefinition('messenger.handler_resolver')->getArgument(0)); + $handlerLocatorDefinition = $container->getDefinition($container->getDefinition("$busId.messenger.handler_resolver")->getArgument(0)); $handlerMapping = $handlerLocatorDefinition->getArgument(0); $this->assertArrayHasKey('handler.'.DummyMessage::class, $handlerMapping); @@ -138,6 +205,7 @@ public function testGetClassesAndMethodsAndPrioritiesFromTheSubscriber() public function testThrowsExceptionIfTheHandlerMethodDoesNotExist() { $container = $this->getContainerBuilder(); + $container->register('message_bus', MessageBusInterface::class)->addTag('messenger.bus'); $container ->register(HandlerMappingWithNonExistentMethod::class, HandlerMappingWithNonExistentMethod::class) ->addTag('messenger.message_handler') @@ -149,6 +217,7 @@ public function testThrowsExceptionIfTheHandlerMethodDoesNotExist() public function testItRegistersReceivers() { $container = $this->getContainerBuilder(); + $container->register('message_bus', MessageBusInterface::class)->addTag('messenger.bus'); $container->register(AmqpReceiver::class, AmqpReceiver::class)->addTag('messenger.receiver', array('alias' => 'amqp')); (new MessengerPass())->process($container); @@ -159,6 +228,7 @@ public function testItRegistersReceivers() public function testItRegistersReceiversWithoutTagName() { $container = $this->getContainerBuilder(); + $container->register('message_bus', MessageBusInterface::class)->addTag('messenger.bus'); $container->register(AmqpReceiver::class, AmqpReceiver::class)->addTag('messenger.receiver'); (new MessengerPass())->process($container); @@ -326,9 +396,8 @@ public function testRegistersTraceableBusesToCollector() { $dataCollector = $this->getMockBuilder(MessengerDataCollector::class)->getMock(); - $container = $this->getContainerBuilder(); + $container = $this->getContainerBuilder($fooBusId = 'messenger.bus.foo'); $container->register('messenger.data_collector', $dataCollector); - $container->register($fooBusId = 'messenger.bus.foo', MessageBusInterface::class)->addTag('messenger.bus'); $container->setParameter('kernel.debug', true); (new MessengerPass())->process($container); @@ -340,8 +409,7 @@ public function testRegistersTraceableBusesToCollector() public function testRegistersMiddlewareFromServices() { - $container = $this->getContainerBuilder(); - $container->register($fooBusId = 'messenger.bus.foo', MessageBusInterface::class)->setArgument(0, array())->addTag('messenger.bus'); + $container = $this->getContainerBuilder($fooBusId = 'messenger.bus.foo'); $container->register('messenger.middleware.allow_no_handler', AllowNoHandlerMiddleware::class)->setAbstract(true); $container->register('middleware_with_factory', UselessMiddleware::class)->addArgument('some_default')->setAbstract(true); $container->register('middleware_with_factory_using_default', UselessMiddleware::class)->addArgument('some_default')->setAbstract(true); @@ -388,8 +456,7 @@ public function testRegistersMiddlewareFromServices() */ public function testCannotRegistersAnUndefinedMiddleware() { - $container = $this->getContainerBuilder(); - $container->register($fooBusId = 'messenger.bus.foo', MessageBusInterface::class)->setArgument(0, array())->addTag('messenger.bus'); + $container = $this->getContainerBuilder($fooBusId = 'messenger.bus.foo'); $container->setParameter($middlewareParameter = $fooBusId.'.middleware', array( array('id' => 'not_defined_middleware', 'arguments' => array()), )); @@ -403,9 +470,8 @@ public function testCannotRegistersAnUndefinedMiddleware() */ public function testMiddlewareFactoryDefinitionMustBeAbstract() { - $container = $this->getContainerBuilder(); + $container = $this->getContainerBuilder($fooBusId = 'messenger.bus.foo'); $container->register('not_an_abstract_definition', UselessMiddleware::class); - $container->register($fooBusId = 'messenger.bus.foo', MessageBusInterface::class)->setArgument(0, array())->addTag('messenger.bus', array('name' => 'foo')); $container->setParameter($middlewareParameter = $fooBusId.'.middleware', array( array('id' => 'not_an_abstract_definition', 'arguments' => array('foo')), )); @@ -413,18 +479,58 @@ public function testMiddlewareFactoryDefinitionMustBeAbstract() (new MessengerPass())->process($container); } - private function getContainerBuilder(): ContainerBuilder + public function testItRegistersTheDebugCommand() + { + $container = $this->getContainerBuilder($commandBusId = 'command_bus'); + $container->register($queryBusId = 'query_bus', MessageBusInterface::class)->setArgument(0, array())->addTag('messenger.bus'); + $container->register($emptyBus = 'empty_bus', MessageBusInterface::class)->setArgument(0, array())->addTag('messenger.bus'); + $container->register('messenger.middleware.call_message_handler', HandleMessageMiddleware::class) + ->addArgument(null) + ->setAbstract(true) + ; + + $container->register('console.command.messenger_debug', DebugCommand::class)->addArgument(array()); + + $middlewares = array(array('id' => 'call_message_handler')); + + $container->setParameter($commandBusId.'.middleware', $middlewares); + $container->setParameter($queryBusId.'.middleware', $middlewares); + + $container->register(DummyCommandHandler::class)->addTag('messenger.message_handler', array('bus' => $commandBusId)); + $container->register(DummyQueryHandler::class)->addTag('messenger.message_handler', array('bus' => $queryBusId)); + $container->register(MultipleBusesMessageHandler::class) + ->addTag('messenger.message_handler', array('bus' => $commandBusId)) + ->addTag('messenger.message_handler', array('bus' => $queryBusId)) + ; + + (new ResolveClassPass())->process($container); + (new MessengerPass())->process($container); + + $this->assertEquals(array( + $commandBusId => array( + DummyCommand::class => array(DummyCommandHandler::class), + MultipleBusesMessage::class => array(MultipleBusesMessageHandler::class), + ), + $queryBusId => array( + DummyQuery::class => array(DummyQueryHandler::class), + MultipleBusesMessage::class => array(MultipleBusesMessageHandler::class), + ), + $emptyBus => array(), + ), $container->getDefinition('console.command.messenger_debug')->getArgument(0)); + } + + private function getContainerBuilder(string $busId = 'message_bus'): ContainerBuilder { $container = new ContainerBuilder(); $container->setParameter('kernel.debug', true); - $container - ->register('messenger.sender_locator', ServiceLocator::class) - ->addArgument(new Reference('service_container')) - ; + $container->register($busId, MessageBusInterface::class)->addTag('messenger.bus')->setArgument(0, array()); + if ('message_bus' !== $busId) { + $container->setAlias('message_bus', $busId); + } $container - ->register('messenger.handler_resolver', ContainerHandlerLocator::class) + ->register('messenger.sender_locator', ServiceLocator::class) ->addArgument(new Reference('service_container')) ; diff --git a/src/Symfony/Component/Messenger/Tests/Fixtures/DummyCommand.php b/src/Symfony/Component/Messenger/Tests/Fixtures/DummyCommand.php new file mode 100644 index 0000000000000..c69375a7c0847 --- /dev/null +++ b/src/Symfony/Component/Messenger/Tests/Fixtures/DummyCommand.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Tests\Fixtures; + +class DummyCommand +{ +} diff --git a/src/Symfony/Component/Messenger/Tests/Fixtures/DummyCommandHandler.php b/src/Symfony/Component/Messenger/Tests/Fixtures/DummyCommandHandler.php new file mode 100644 index 0000000000000..1665bb0da9148 --- /dev/null +++ b/src/Symfony/Component/Messenger/Tests/Fixtures/DummyCommandHandler.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Tests\Fixtures; + +class DummyCommandHandler +{ + public function __invoke(DummyCommand $command) + { + } +} diff --git a/src/Symfony/Component/Messenger/Tests/Fixtures/DummyQuery.php b/src/Symfony/Component/Messenger/Tests/Fixtures/DummyQuery.php new file mode 100644 index 0000000000000..22d0957ea8998 --- /dev/null +++ b/src/Symfony/Component/Messenger/Tests/Fixtures/DummyQuery.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Tests\Fixtures; + +class DummyQuery +{ +} diff --git a/src/Symfony/Component/Messenger/Tests/Fixtures/DummyQueryHandler.php b/src/Symfony/Component/Messenger/Tests/Fixtures/DummyQueryHandler.php new file mode 100644 index 0000000000000..827de27e3fc51 --- /dev/null +++ b/src/Symfony/Component/Messenger/Tests/Fixtures/DummyQueryHandler.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Tests\Fixtures; + +class DummyQueryHandler +{ + public function __invoke(DummyQuery $query) + { + } +} diff --git a/src/Symfony/Component/Messenger/Tests/Fixtures/MultipleBusesMessage.php b/src/Symfony/Component/Messenger/Tests/Fixtures/MultipleBusesMessage.php new file mode 100644 index 0000000000000..663d713a5d504 --- /dev/null +++ b/src/Symfony/Component/Messenger/Tests/Fixtures/MultipleBusesMessage.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Tests\Fixtures; + +class MultipleBusesMessage +{ +} diff --git a/src/Symfony/Component/Messenger/Tests/Fixtures/MultipleBusesMessageHandler.php b/src/Symfony/Component/Messenger/Tests/Fixtures/MultipleBusesMessageHandler.php new file mode 100644 index 0000000000000..3deea4f455069 --- /dev/null +++ b/src/Symfony/Component/Messenger/Tests/Fixtures/MultipleBusesMessageHandler.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Tests\Fixtures; + +class MultipleBusesMessageHandler +{ + public function __invoke(MultipleBusesMessage $message) + { + } +} From fd810cdee02920d6f1797980e829588e69da877d Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Sun, 20 May 2018 10:26:34 +0100 Subject: [PATCH 70/76] Uses `protected` for test functions --- .../Component/Messenger/Tests/Command/DebugCommandTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Messenger/Tests/Command/DebugCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/DebugCommandTest.php index a707f6a99dca5..deb01cf474d97 100644 --- a/src/Symfony/Component/Messenger/Tests/Command/DebugCommandTest.php +++ b/src/Symfony/Component/Messenger/Tests/Command/DebugCommandTest.php @@ -26,12 +26,12 @@ */ class DebugCommandTest extends TestCase { - public function setUp() + protected function setUp() { putenv('COLUMNS='.(119 + strlen(PHP_EOL))); } - public function tearDown() + protected function tearDown() { putenv('COLUMNS='); } From 00603bf0109f076c3feaf9827678b2aa83d36328 Mon Sep 17 00:00:00 2001 From: Philip Ardery Date: Fri, 18 May 2018 17:30:37 -0700 Subject: [PATCH 71/76] Supress deprecation notices thrown when getting private servies from container in tests --- .../PhpUnit/DeprecationErrorHandler.php | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php index 29b1960798b8c..f614ac99001b7 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php @@ -109,6 +109,30 @@ public static function register($mode = 0) } $trace = debug_backtrace(true); + + // Silence deprecation warnings about private service accessed + // from the service container if done so from a Test class. + // As of Symfony 4.1, there is a new TestContainer that allows + // fetching of private services within tests, so we no longer + // need to warn about this behavior. + // + // NOTE: the event at the top of the stack $trace (index 0) should + // always be the PhpUnitBridge's DeprecationErrorHandler; the + // second event (index 1) should be the trigger_error() event; + // the third event (index 2) should be the actual source of the + // triggered deprecation notice; and the fourth event (index 3) + // represents the action that called the deprecated code. In the + // scenario that we want to suppress, the 4th event will be an + // object instance of \PHPUnit\Framework\TestCase. + if (isset($trace[3]['object'])) { + $isPrivateServiceNotice = false !== strpos($msg, ' service is private, '); + $isNoticeForContainerGetHasUsage = 'Symfony\Component\DependencyInjection\Container' === $trace[2]['class'] && in_array($trace[2]['function'], array('get', 'has')); + $noticeWasTriggeredByPhpUnitTest = $trace[3]['object'] instanceof \PHPUnit\Framework\TestCase; + if ($isPrivateServiceNotice && $isNoticeForContainerGetHasUsage && $noticeWasTriggeredByPhpUnitTest) { + return false; + } + } + $group = 'other'; $isVendor = DeprecationErrorHandler::MODE_WEAK_VENDORS === $mode && $inVendors($file); From f2419ddda6d10102ecb5217ddb6ae343a36c04c8 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 21 May 2018 12:48:22 +0200 Subject: [PATCH 72/76] fixed bad merge --- .../Tests/DependencyInjection/ConfigurationTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index bc0baea128b10..e694454f0bcbe 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -142,7 +142,6 @@ public function testInvalidValueTrustedProxies() )); } ->>>>>>> 3.4 public function testAssetsCanBeEnabled() { $processor = new Processor(); From 2ce15f9179583277094e224a62fab5eedf847bb5 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 21 May 2018 12:58:57 +0200 Subject: [PATCH 73/76] removed obsolete tests --- .../DependencyInjection/ConfigurationTest.php | 63 ------------------- 1 file changed, 63 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index e694454f0bcbe..70ad9f3c15fd9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -79,69 +79,6 @@ public function getTestInvalidSessionName() ); } - /** - * @dataProvider getTestValidTrustedProxiesData - */ - public function testValidTrustedProxies($trustedProxies, $processedProxies) - { - $processor = new Processor(); - $configuration = new Configuration(true); - $config = $processor->processConfiguration($configuration, array(array( - 'secret' => 's3cr3t', - 'trusted_proxies' => $trustedProxies, - ))); - - $this->assertEquals($processedProxies, $config['trusted_proxies']); - } - - public function getTestValidTrustedProxiesData() - { - return array( - array(array('127.0.0.1'), array('127.0.0.1')), - array(array('::1'), array('::1')), - array(array('127.0.0.1', '::1'), array('127.0.0.1', '::1')), - array(null, array()), - array(false, array()), - array(array(), array()), - array(array('10.0.0.0/8'), array('10.0.0.0/8')), - array(array('::ffff:0:0/96'), array('::ffff:0:0/96')), - array(array('0.0.0.0/0'), array('0.0.0.0/0')), - ); - } - - /** - * @group legacy - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - */ - public function testInvalidTypeTrustedProxies() - { - $processor = new Processor(); - $configuration = new Configuration(true); - $processor->processConfiguration($configuration, array( - array( - 'secret' => 's3cr3t', - 'trusted_proxies' => 'Not an IP address', - ), - )); - } - - /** - * @group legacy - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - */ - public function testInvalidValueTrustedProxies() - { - $processor = new Processor(); - $configuration = new Configuration(true); - - $processor->processConfiguration($configuration, array( - array( - 'secret' => 's3cr3t', - 'trusted_proxies' => array('Not an IP address'), - ), - )); - } - public function testAssetsCanBeEnabled() { $processor = new Processor(); From dff61b82ffd5cbe96571ae2bc687e2f51096fcaf Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 21 May 2018 13:07:53 +0200 Subject: [PATCH 74/76] fixed bad merge --- src/Symfony/Bridge/Doctrine/composer.json | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/composer.json b/src/Symfony/Bridge/Doctrine/composer.json index df9f7c671a869..9ad37c23e3615 100644 --- a/src/Symfony/Bridge/Doctrine/composer.json +++ b/src/Symfony/Bridge/Doctrine/composer.json @@ -24,7 +24,6 @@ "require-dev": { "symfony/stopwatch": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0", -<<<<<<< HEAD "symfony/form": "~3.4|~4.0", "symfony/http-kernel": "~3.4|~4.0", "symfony/property-access": "~3.4|~4.0", @@ -34,17 +33,6 @@ "symfony/expression-language": "~3.4|~4.0", "symfony/validator": "~3.4|~4.0", "symfony/translation": "~3.4|~4.0", -======= - "symfony/form": "^3.3.10|~4.0", - "symfony/http-kernel": "~2.8|~3.0|~4.0", - "symfony/property-access": "~2.8|~3.0|~4.0", - "symfony/property-info": "~2.8|3.0|~4.0", - "symfony/proxy-manager-bridge": "~2.8|~3.0|~4.0", - "symfony/security": "^2.8.31|^3.3.13|~4.0", - "symfony/expression-language": "~2.8|~3.0|~4.0", - "symfony/validator": "^3.2.5|~4.0", - "symfony/translation": "~2.8|~3.0|~4.0", ->>>>>>> 3.4 "doctrine/data-fixtures": "1.0.*", "doctrine/dbal": "~2.4", "doctrine/orm": "^2.4.5" From c577609a664d3ac1d6b8e5eb9bbfbb098a1154df Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 21 May 2018 16:27:36 +0200 Subject: [PATCH 75/76] updated CHANGELOG for 4.1.0-BETA2 --- CHANGELOG-4.1.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/CHANGELOG-4.1.md b/CHANGELOG-4.1.md index d5f851276a7b2..5437c76fd6bad 100644 --- a/CHANGELOG-4.1.md +++ b/CHANGELOG-4.1.md @@ -7,6 +7,50 @@ in 4.1 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v4.1.0...v4.1.1 +* 4.1.0-BETA2 (2018-05-21) + + * bug #27312 Supress deprecation notices thrown when getting private servies from container in tests (arderyp) + * feature #27275 [Messenger] Allow to scope handlers per bus (ogizanagi, sroze) + * bug #27264 [Validator] Use strict type in URL validator (mimol91) + * bug #27267 [DependencyInjection] resolve array env vars (jamesthomasonjr) + * bug #26781 [Form] Fix precision of MoneyToLocalizedStringTransformer's divisions on transform() (syastrebov) + * bug #27270 [Routing] Fix adding name prefix to canonical route names (ismail1432) + * bug #27286 [Translation] Add Occitan plural rule (kylekatarnls) + * bug #27271 [DI] Allow defining bindings on ChildDefinition (nicolas-grekas) + * bug #27246 Disallow invalid characters in session.name (ostrolucky) + * feature #27230 [Messenger] Select alternatives on missing receiver arg or typo (yceruto) + * bug #27287 [PropertyInfo] fix resolving parent|self type hints (nicolas-grekas) + * bug #27281 [HttpKernel] Fix dealing with self/parent in ArgumentMetadataFactory (fabpot) + * bug #24805 [Security] Fix logout (MatTheCat) + * bug #27265 [DI] Shared services should not be inlined in non-shared ones (nicolas-grekas) + * bug #27141 [Process] Suppress warnings when open_basedir is non-empty (cbj4074) + * bug #27250 [Session] limiting :key for GET_LOCK to 64 chars (oleg-andreyev) + * feature #27128 [Messenger] Middleware factories support in config (ogizanagi) + * bug #27214 [HttpKernel] Fix services are no longer injected into __invoke controllers method (ogizanagi) + * bug #27237 [Debug] Fix populating error_get_last() for handled silent errors (nicolas-grekas) + * bug #27232 [Cache][Lock] Fix usages of error_get_last() (nicolas-grekas) + * bug #27236 [Filesystem] Fix usages of error_get_last() (nicolas-grekas) + * feature #27202 [Messenger] Improve the profiler panel (ogizanagi) + * bug #27191 [DI] Display previous error messages when throwing unused bindings (nicolas-grekas) + * bug #27231 [FrameworkBundle] Fix cache:clear on vagrant (nicolas-grekas) + * bug #27222 [WebProfilerBundle][Cache] Fix misses calculation when calling getItems (fsevestre) + * bug #27227 [HttpKernel] Handle NoConfigurationException "onKernelException()" (nicolas-grekas) + * feature #27034 [Messenger][DX] Uses custom method names for handlers (sroze) + * bug #27228 [Messenger] Remove autoconfiguration for Sender/ReceiverInterface (kbond) + * bug #27229 [Messenger] Rename tag attribute "name" by "alias" (yceruto) + * bug #27224 [Messenger] Make sure default receiver name is set before command configuration (yceruto) + * feature #27225 [Messenger] Autoconfiguring TransportFactoryInterface classes (yceruto) + * bug #27220 [Messenger] Fix new AMQP Transport test with Envelope & fix contract (ogizanagi) + * bug #27184 [Messenger] Fix return senders based on the message parents/interfaces (yceruto) + * feature #27182 [Messenger] Re-introduce wrapped message configuration (with fix) (sroze, ogizanagi) + * bug #27209 [Workflow] add is deprecated since Symfony 4.1. Use addWorkflow() instead (xkobal) + * feature #26803 [Messenger] Add debug:messenger CLI command (ro0NL, sroze) + * bug #27189 [Profiler] Fix dump makes toolbar disappear (ogizanagi) + * bug #27199 [Messenger] Fix default bus name (ogizanagi) + * bug #27198 [Messenger] Fix the transport factory after moving it (sroze) + * bug #27197 [Messenger] Fix AMQP Transport factory & TransportFactoryInterface (ogizanagi) + * bug #27196 [Messenger] Fix AMQP Transport (yceruto) + * 4.1.0-BETA1 (2018-05-07) * feature #26945 [Messenger] Support configuring messages when dispatching (ogizanagi) From ae8513b01ce987c981c35738fef6d797276abbf6 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 21 May 2018 16:27:45 +0200 Subject: [PATCH 76/76] updated VERSION for 4.1.0-BETA2 --- 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 a408ae1888b99..ae3ea7243749f 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -63,12 +63,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private $requestStackSize = 0; private $resetServices = false; - const VERSION = '4.1.0-DEV'; + const VERSION = '4.1.0-BETA2'; const VERSION_ID = 40100; const MAJOR_VERSION = 4; const MINOR_VERSION = 1; const RELEASE_VERSION = 0; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = 'BETA2'; const END_OF_MAINTENANCE = '01/2019'; const END_OF_LIFE = '07/2019';