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 1ad3aa1615c9..d9fafdff13a3 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 f68a60143a74..4a9acb5be9ac 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 6c1cc569a329..6d681f32248b 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 f5c97d6bb9f8..2a0b7a0268d7 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 0d994c5f8f8f..ff6351e7078e 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 d7ce01d39fc2..eebf2b0f1b7f 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 0c4bb18f31ca..523c35db0f11 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 000000000000..165a795d71ab --- /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 ddb385af52ae..2da18cb06692 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 a142e1f00995..93b1bd7cbaa3 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 779d365dc47e..2c20533bfb8a 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 47ded446bf06..156eee9839cf 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 000000000000..066cf5cda7e4 --- /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 a45b2b9d02b6..cfaef24ac3a8 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 e1309d477bd2..7e8e075a833f 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 000000000000..a75c722484c1 --- /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 904eafcdd94f..f0779b184485 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 e8249b307f80..40eecf9c54e4 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 1553f304dabe..7088cd87835e 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 165a795d71ab..cd5fcdb4899a 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 2da18cb06692..3ffbb561591e 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 156eee9839cf..ac1c72f1b9ad 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 28eccb9a8987..145f0543de9e 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 4314f17204a1..a20fde031316 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 8ab7b7917219..d47c1be5526a 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 eebf2b0f1b7f..8d94d32aa83b 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 000000000000..53a98e2263a0 --- /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 000000000000..26f3261577ca --- /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 cd5fcdb4899a..583ec03baefb 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 3ffbb561591e..29fb4ae4aa3e 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 d8f4b61699a9..dafdc40219f9 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 cb71bbb8de8f..40da27e075da 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 000000000000..544538dcf879 --- /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 1fd76db8fee2..2af6577f485a 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 40da27e075da..21fe28375f6f 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 544538dcf879..f41747dc0f64 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 7088cd87835e..0c5967b4e75c 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 8c1c32194fde..3dfca6a06970 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 cb71bbb8de8f..da87f2ea15c1 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 94684aacfbce..72dc2aa907f2 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 62f63dc05894..107234dad956 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 b230a9c6df60..37c9aefed4a4 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 eeba363cc87c..9aabe794a11a 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 1b1298da63d3..c713a589ad53 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 8af87a2a4514..4b10d6445fc5 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 000000000000..14778593c4ba --- /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 000000000000..c19bc8436286 --- /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 000000000000..2561a127546c --- /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 f0779b184485..c467a6f79dac 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 1d441ea568ff..8c2b91d1af3a 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 000000000000..1b6180857b31 --- /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 4de6e4257580..ebaf8525c040 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 3b168367cdd3..e588d9256bd3 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 6398aff36168..aa22741c397b 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 000000000000..6ebcc8ddfa0c --- /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 37c9aefed4a4..48217ba731b5 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 000000000000..053275b6e714 --- /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 000000000000..6fd6962994f4 --- /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 2bd2ba8af22e..b2a44b5fa672 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 5ce9c457e984..c01ffdadb7f0 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 2045764216ca..4d0b779e3b3f 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 54859a58acf1..8848b022f6aa 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 2115b1a81dc2..b7231a5d47fa 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 f317c6a5bfe9..a34be3bfc2ce 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 7a516744e189..e5c51335b3b8 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 2e227c0f2f71..214b773092e8 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 7599d8dadc22..bdfa6fe188d6 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 e61ef0389531..0e6fbff8ee34 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 523c35db0f11..397c52dec2a2 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 4a05afe7707f..37b1978233e9 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 dc61466bbf40..420eb8fa63a4 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 1c29fbe43abe..0f61fe3428d7 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 93b1bd7cbaa3..ba36b80fe2af 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 a232dfbf10e3..026a32103316 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 658b9e5b5e68..1dce6fdae33c 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 65c2ac55e888..cfb30090e5df 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 000000000000..478e197080ad --- /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 2033f3a770fd..918b61a52a18 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 14778593c4ba..4d5f7a02a9d5 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 c467a6f79dac..89fa8e04fa23 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 40eecf9c54e4..ba40eb3f9a12 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 8e4895d3babc..4da1e5a63020 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 ecf0c5658cce..9620e0189dac 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 ff6351e7078e..bbdf232fef8c 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 8c1c32194fde..08e2f4ead061 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 5d2d4429e4a9..901dc06ffaee 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 024523e15585..9a895cb52325 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 16e486afafe2..65f5ceb80fd0 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 72dc2aa907f2..62892c59f059 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 194772814347..9789ec038cf4 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 37c9aefed4a4..46983486d707 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 9b62457626c5..506fe234c6ce 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 caf952526439..6278be85c57d 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 fb02ca7b866a..2a9c70b1c534 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 000000000000..557b958ddc62 --- /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 26f3261577ca..3d003a270d06 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 583ec03baefb..3edefd0ab1c8 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 ceef45aa0b18..962fb25cb94c 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 c08ad7b3903a..9f2075057a14 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 caaf80f86b89..959f2ba0d113 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 c08ad7b3903a..af449c1a5498 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 8d94d32aa83b..cbed3c89f859 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 9789ec038cf4..81f84617064c 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 eeb5d585f5cd..d49a38421b1d 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 cd76e56caab8..490b595b9415 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 b7d64994cec8..596b6188f66c 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 62892c59f059..e64d10f1953e 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 000000000000..4eca2423aa47 --- /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 1767f4dab967..adbd0e2a9f67 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 fa853e094090..bc41753a7b34 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 9789ec038cf4..230676d8eef8 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 6a751482bff0..ba4ae3ee7987 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 eeb5d585f5cd..ac32461da6a2 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 e64d10f1953e..06b48a50c5ac 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 81f84617064c..88e468770c00 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 4eca2423aa47..de489b545f0e 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 d49a38421b1d..5e35b18590ca 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 bbb71c15dd92..650f36dc15b7 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 413b476f2938..596fcdd84d2c 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 47f4b422216b..43114dbcc07d 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 91d2d979c16b..7ebe982e6d0b 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 2c4c15f41042..44f6e45e924c 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 1c84ff32c0b7..ab1cda6702be 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 8fa436e20178..8390d16332fa 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 1c3451bbebf4..59497dc96198 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 9fd20649289d..452845cea8e3 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 14c6a8d0fa58..91763e5a9f33 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 11228f7246d3..9b35cb411632 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 28478b3db7e4..73b548215d67 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 8090df99075f..638cbd0fe061 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 24679dcaab24..f491bf2ac4c9 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 9ecb2a21c3de..78a8e3c327f8 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 921ae2829c5f..a0e9b636c5f3 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 21a3d5036ad2..9fbca4379688 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 2cadba0ba9e6..a2c606c0362a 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 5ae3d52cc30b..7b30aed38105 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 b35727962eac..b4af82ddf68d 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 e91a0b0c70e4..cc7efc848747 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 6f203d3a987c..1602de4c261f 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 2ddc6e7a5ee1..a667705f5081 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 243c3c5c5a7c..2d5f0ca5ad59 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 26fd4f946ccc..de1ab1a249ed 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 8edd6b5c8979..68b4154b10da 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 1768dda88542..40a2ae047e81 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 71aa162f70b7..461495ed510a 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 6916297b8c17..dd057d2ee830 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 d63c6138f798..a58613cefe48 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 80ae75caa2b8..b9859452b158 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 fb8c7a1761cb..00440702279a 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 944a7b3133f3..d9a15d5ac0de 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 3811e56548a0..a0765861643b 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 ecf60dc8884a..e2b15d1aa1c4 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 4dad27fbf6aa..e0dbb2bd962c 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 0489ab500a1d..47e986f51e50 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 dd0dd1fa46a9..d7773861445f 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 9e9e1e796fcc..310cec8f1546 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 94e1e2effe16..308f95032413 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 1939fb571396..1af7a326935d 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 e056862b8e67..4671b85469d7 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 4eb4aef0c0fa..d8f4a74d8696 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 b8e05d9e417c..9fc6177c6bd7 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 287d6fb58316..d0317358e89a 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 908efe877101..d8befbbf8dca 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 96cd8878a809..f9bc41d6a1b5 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 0fe44d62fea1..ed98f4cfa43f 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 91f54490ac9e..d88593e3e747 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 000000000000..9e5bb0c92b63 --- /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 ba40eb3f9a12..9dc93a9d1521 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 4da1e5a63020..8a2946ee4277 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 9620e0189dac..b60d220b15ce 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 f54d3589f7df..b5e5a2b5ca06 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 4b8eaea64e66..3fe12e867a82 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 5c3b2a2d9cb8..0f6d0a269e6c 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 19f95e26be69..0f4e025b22bd 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 e2a67684fcda..65ece9837901 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 082d8d534e17..39b29775ccd8 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 9a23d93c9868..97fe728bfd70 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 65f32ecf2735..935c43209d9d 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 9837a057a6ae..4d60aa15f7ad 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 1b12a581f3ba..77c0e479ae0a 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 3cbc62131fa2..49bbd0f9c1cc 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 b21b3ee8df76..a29d8fada010 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 a20a120d0710..6505d5a03493 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 3361be296e26..469aee8f5fe6 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 c64348ed1de3..a79e78b6b2a4 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 adaa4044f453..4abe6143152c 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 9d61c9cd8328..10b4a7a9b4dc 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 222597ed10f4..292e117d9f12 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 dfd66516062c..0825ee6ea989 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 000000000000..0db646fdc888 --- /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 a20fde031316..ce65f52bef12 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 c08ad7b3903a..6b6d266036bb 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 96a13c8cb835..c5e05b11a7ec 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 000000000000..d6cc86bd37d6 --- /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 51080f50368a..5f3b2b23028a 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 88b43585361a..f63df5bbbe4b 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 000000000000..74431414ba99 --- /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 83c6213348f0..e279ef3bba06 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 8d94d32aa83b..3461e8defe63 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 03401e248294..a272ab33276b 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 9789ec038cf4..1089027c28d9 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 eeb5d585f5cd..0bcec20fcbce 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 760366c6943a..05d8d787aa5c 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 d47287a1fbc6..4dae039c11fc 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 f363a64829c2..61460ca2e0f7 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 a124e472aca8..eefb0cefd4ff 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 23a1915fd777..18c0fb7c5987 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 84719e2c82fa..4d14ca96db41 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 3527e1289568..0ea23f673460 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 3ffe2876e88d..9c526f88daa9 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 b7c1407c1cc5..b044ccba98e7 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 13d096d97e95..e9f8fe66d639 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 dc87681c3751..d45d7b87f04a 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 855bed9b6899..371baa704a83 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 000000000000..7eeb7c21171c --- /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 000000000000..d90f774abde2 --- /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 000000000000..48fd4ed6cc3c --- /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 000000000000..1dddfca2f815 --- /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 e765b16bef86..90edcf6e38ce 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 62b0071212e5..376194d3a6c8 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 e767d123cb03..fc97410d4e69 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 93e5a54793f3..588f0028a494 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 48fd4ed6cc3c..60e9cb89a229 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 1c84ff32c0b7..ab1cda6702be 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 8fa436e20178..8390d16332fa 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 1c3451bbebf4..59497dc96198 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 9fd20649289d..452845cea8e3 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 962fb25cb94c..ac5c36d2fd83 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 6aa70d781cae..a11b7f73d89c 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 63e3ac36bf82..a1e244c9e45d 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 24679dcaab24..f491bf2ac4c9 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 1d3975bf8794..629874d19cd1 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 21a3d5036ad2..9fbca4379688 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 2cadba0ba9e6..f9c4613e591c 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 d1e7af206804..0424850e144c 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 15bfbc5a0777..6245a21b5f14 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 b4b449f35861..bf0db5ad5651 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 243c3c5c5a7c..2d5f0ca5ad59 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 330496827cfc..8d5c4fe10716 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 86bc2f7643db..b2c8e36be44e 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 6916297b8c17..dd057d2ee830 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 e8a9a165d673..3aec14695131 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 5b74523d631b..9b3bfedcc812 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 944a7b3133f3..d9a15d5ac0de 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 3811e56548a0..a0765861643b 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 25d6ff6a9d64..ec03fef8cfbc 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 4dad27fbf6aa..e0dbb2bd962c 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 f716ce343239..84b0e3f4f773 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 ce65f52bef12..690826cc432f 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 1d69a4f99f4a..e8dfbd14a169 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 145f0543de9e..32a4a4d89535 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 c5e05b11a7ec..2dbe54b5d938 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 29891140ffdf..51b1184764df 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 3772030e5e3e..808b0ba9cd46 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 2243a76f23ef..da096fcaed64 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 383dbd7ae0c1..2ae8411c2290 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 9aabe794a11a..16fdfd81f108 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 506fe234c6ce..e85453691bd9 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 d97508b31a2f..ffce0cf3a855 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 aa22741c397b..c9d548360c7b 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 6278be85c57d..22cf6bac81e6 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 000000000000..97e3ce44bbaf --- /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 bc9ae5074d08..4e527eb05db1 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 dab463f7e510..647d4c1072e6 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 176e1511f8c2..f8c61dd48aa3 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 b1be5ee11821..91cdf80f8d60 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 4e558eca014e..5993d6e1d47e 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 cd5ba380d9d5..5dcb4c565e76 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 a46617db081d..4a2a3a2fd884 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 082870249ba4..3529a6f2b1bb 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 274602a6ad47..dce640e31dad 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 1dce6fdae33c..b5ea2acd937d 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 36a41c039b9d..c909da17bbdb 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 bbdb8af0076e..0a7937916025 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 dc84e29345b6..dfcac06b40b2 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 ed517e692cf7..dd9af9db23f7 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 c3d59b29be49..fff4154b17d5 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 390a5b281e99..68a092d7019f 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 d55bfbc143d1..e37600c3ebb8 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 2e8c20312c9e..1d0858c2def9 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 f94284d2e072..0fece9802e20 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 20d02abac37f..e5b5a39a3826 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 e1937deb671e..48af8a77b403 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 14de4ad38afb..3e7b75ba127e 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 d670186cb7dc..90cc9523fb70 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 320892095cb1..35c7456638ea 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 bde88858ae5a..a604766c0002 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 e5ece89620b7..2b7b11833693 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 7f79e2f4ffc5..7296a098ee0e 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 51038b6b1550..f2e8368455f6 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 c629ca15255f..306e19ad91bb 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 e8dfbd14a169..318af6c8b9a2 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 32a4a4d89535..0b09978d3387 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 f41747dc0f64..38d4e4253cec 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 f2767058c6f0..7836637dde86 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 000000000000..a707f6a99dca --- /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 1b86ffa6295b..6c77704adb90 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 000000000000..c69375a7c084 --- /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 000000000000..1665bb0da914 --- /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 000000000000..22d0957ea899 --- /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 000000000000..827de27e3fc5 --- /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 000000000000..663d713a5d50 --- /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 000000000000..3deea4f45506 --- /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 a707f6a99dca..deb01cf474d9 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 29b1960798b8..f614ac99001b 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 bc0baea128b1..e694454f0bcb 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 e694454f0bcb..70ad9f3c15fd 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 df9f7c671a86..9ad37c23e361 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 d5f851276a7b..5437c76fd6ba 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 a408ae1888b9..ae3ea7243749 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';