Merge branch '4.1'
* 4.1: Supress deprecation notices thrown when getting private servies from container in tests Uses `protected` for test functions [Messenger] Allow to scope handlers per bus do not mock the session in token storage tests [DependencyInjection] resolve array env vars Add Occitan plural rule Fix security/* cross-dependencies [Messenger] implement several senders using a ChainSender [Lock] Skip test if posix extension is not installed fix bug when imported routes are prefixed [DI] Allow defining bindings on ChildDefinition use strict compare in url validator Disallow illegal characters like "." in session.name [HttpKernel] do file_exists() check instead of silent notice Select alternatives on missing receiver arg or typo fix rounding from string
This commit is contained in:
commit
1f4100d635
@ -24,6 +24,7 @@
|
||||
"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",
|
||||
@ -33,6 +34,17 @@
|
||||
"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"
|
||||
|
@ -109,6 +109,30 @@ class DeprecationErrorHandler
|
||||
}
|
||||
|
||||
$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);
|
||||
|
||||
|
@ -467,7 +467,16 @@ class Configuration implements ConfigurationInterface
|
||||
->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()
|
||||
@ -986,7 +995,10 @@ class Configuration implements ConfigurationInterface
|
||||
$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 +1006,7 @@ class Configuration implements ConfigurationInterface
|
||||
},
|
||||
array_values($v['sender'])
|
||||
);
|
||||
$newConfig[$v['message-class']]['send-and-handle'] = $v['send-and-handle'] ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1006,6 +1019,7 @@ class Configuration implements ConfigurationInterface
|
||||
->requiresAtLeastOneElement()
|
||||
->prototype('scalar')->end()
|
||||
->end()
|
||||
->booleanNode('send_and_handle')->defaultFalse()->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
|
@ -63,6 +63,7 @@ use Symfony\Component\Lock\StoreInterface;
|
||||
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;
|
||||
@ -1482,7 +1483,7 @@ class FrameworkExtension extends Extension
|
||||
}
|
||||
|
||||
$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);
|
||||
@ -1494,16 +1495,28 @@ class FrameworkExtension extends Extension
|
||||
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')) {
|
||||
|
@ -7,18 +7,14 @@
|
||||
<services>
|
||||
<defaults public="false" />
|
||||
|
||||
<!-- Handlers -->
|
||||
<service id="messenger.handler_resolver" class="Symfony\Component\Messenger\Handler\Locator\ContainerHandlerLocator">
|
||||
<argument type="service" id="service_container"/>
|
||||
</service>
|
||||
|
||||
<!-- Asynchronous -->
|
||||
<service id="messenger.asynchronous.routing.sender_locator" class="Symfony\Component\Messenger\Asynchronous\Routing\SenderLocator">
|
||||
<argument type="service" id="messenger.sender_locator" />
|
||||
<argument /> <!-- Message to sender ID mapping -->
|
||||
<argument type="collection" /> <!-- Message to sender ID mapping -->
|
||||
</service>
|
||||
<service id="messenger.middleware.route_messages" class="Symfony\Component\Messenger\Asynchronous\Middleware\SendMessageMiddleware">
|
||||
<argument type="service" id="messenger.asynchronous.routing.sender_locator" />
|
||||
<argument type="collection" /> <!-- Message to send and handle mapping -->
|
||||
</service>
|
||||
|
||||
<!-- Message encoding/decoding -->
|
||||
@ -31,7 +27,7 @@
|
||||
<!-- Middleware -->
|
||||
<service id="messenger.middleware.allow_no_handler" class="Symfony\Component\Messenger\Middleware\AllowNoHandlerMiddleware" abstract="true" />
|
||||
<service id="messenger.middleware.call_message_handler" class="Symfony\Component\Messenger\Middleware\HandleMessageMiddleware" abstract="true">
|
||||
<argument type="service" id="messenger.handler_resolver" />
|
||||
<argument /> <!-- Bus handler resolver -->
|
||||
</service>
|
||||
|
||||
<service id="messenger.middleware.validation" class="Symfony\Component\Messenger\Middleware\ValidationMiddleware" abstract="true">
|
||||
|
@ -375,6 +375,7 @@
|
||||
<xsd:element name="sender" type="messenger_routing_sender" />
|
||||
</xsd:choice>
|
||||
<xsd:attribute name="message-class" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="send-and-handle" type="xsd:boolean" default="false"/>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="messenger_routing_sender">
|
||||
|
@ -46,6 +46,105 @@ class ConfigurationTest extends TestCase
|
||||
$this->assertEquals(array('FrameworkBundle:Form'), $config['templating']['form']['resources']);
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
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'),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
>>>>>>> 3.4
|
||||
public function testAssetsCanBeEnabled()
|
||||
{
|
||||
$processor = new Processor();
|
||||
|
@ -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',
|
||||
),
|
||||
),
|
||||
|
@ -9,11 +9,11 @@
|
||||
<framework:messenger>
|
||||
<framework:routing message-class="Symfony\Component\Messenger\Tests\Fixtures\DummyMessage">
|
||||
<framework:sender service="amqp" />
|
||||
<framework:sender service="audit" />
|
||||
</framework:routing>
|
||||
<framework:routing message-class="Symfony\Component\Messenger\Tests\Fixtures\SecondMessage">
|
||||
<framework:routing message-class="Symfony\Component\Messenger\Tests\Fixtures\SecondMessage" send-and-handle="true">
|
||||
<framework:sender service="amqp" />
|
||||
<framework:sender service="audit" />
|
||||
<framework:sender service="null" />
|
||||
</framework:routing>
|
||||
<framework:routing message-class="*">
|
||||
<framework:sender service="amqp" />
|
||||
|
@ -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
|
||||
|
@ -558,14 +558,21 @@ abstract class FrameworkExtensionTest extends TestCase
|
||||
{
|
||||
$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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -121,12 +121,4 @@ class ChildDefinition extends Definition
|
||||
{
|
||||
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.');
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ class ResolveChildDefinitionsPass extends AbstractRecursivePass
|
||||
$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();
|
||||
|
@ -1363,6 +1363,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
||||
}
|
||||
$envPlaceholders = $bag instanceof EnvPlaceholderParameterBag ? $bag->getEnvPlaceholders() : $this->envPlaceholders;
|
||||
|
||||
$completed = false;
|
||||
foreach ($envPlaceholders as $env => $placeholders) {
|
||||
foreach ($placeholders as $placeholder) {
|
||||
if (false !== stripos($value, $placeholder)) {
|
||||
@ -1373,14 +1374,19 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -355,6 +355,27 @@ class ResolveChildDefinitionsPassTest extends TestCase
|
||||
$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();
|
||||
|
@ -665,17 +665,49 @@ class ContainerBuilderTest extends TestCase
|
||||
putenv('DUMMY_ENV_VAR');
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)%".
|
||||
*/
|
||||
public function testCompileWithArrayResolveEnv()
|
||||
{
|
||||
$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', '%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(json:ARRAY)" of type array inside string value "ABC %env(json:ARRAY)%".
|
||||
*/
|
||||
public function testCompileWithArrayInStringResolveEnv()
|
||||
{
|
||||
putenv('ARRAY={"foo":"bar"}');
|
||||
|
||||
$container = new ContainerBuilder();
|
||||
$container->setParameter('foo', 'ABC %env(json:ARRAY)%');
|
||||
$container->compile(true);
|
||||
|
||||
putenv('ARRAY');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1415,11 +1447,3 @@ class B
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class TestingEnvPlaceholderParameterBag extends EnvPlaceholderParameterBag
|
||||
{
|
||||
public function get($name)
|
||||
{
|
||||
return 'env(array)' === strtolower($name) ? array(123) : parent::get($name);
|
||||
}
|
||||
}
|
||||
|
@ -78,6 +78,16 @@ class MoneyToLocalizedStringTransformerTest extends TestCase
|
||||
$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));
|
||||
}
|
||||
}
|
||||
|
@ -465,7 +465,7 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
|
||||
$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;
|
||||
@ -528,7 +528,7 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
|
||||
}
|
||||
}
|
||||
|
||||
if (null === $oldContainer) {
|
||||
if (null === $oldContainer && file_exists($cache->getPath())) {
|
||||
$errorLevel = error_reporting(\E_ALL ^ \E_WARNING);
|
||||
try {
|
||||
$oldContainer = include $cache->getPath();
|
||||
|
@ -710,6 +710,7 @@ class NumberFormatter
|
||||
} elseif (isset(self::$customRoundingList[$roundingModeAttribute])) {
|
||||
$roundingCoef = pow(10, $precision);
|
||||
$value *= $roundingCoef;
|
||||
$value = (float) (string) $value;
|
||||
|
||||
switch ($roundingModeAttribute) {
|
||||
case self::ROUND_CEILING:
|
||||
|
@ -428,6 +428,7 @@ abstract class AbstractNumberFormatterTest extends TestCase
|
||||
// array(1.125, '1.13'),
|
||||
array(1.127, '1.13'),
|
||||
array(1.129, '1.13'),
|
||||
array(1020 / 100, '10.20'),
|
||||
);
|
||||
}
|
||||
|
||||
@ -451,6 +452,7 @@ abstract class AbstractNumberFormatterTest extends TestCase
|
||||
array(1.125, '1.12'),
|
||||
array(1.127, '1.13'),
|
||||
array(1.129, '1.13'),
|
||||
array(1020 / 100, '10.20'),
|
||||
);
|
||||
}
|
||||
|
||||
@ -474,6 +476,7 @@ abstract class AbstractNumberFormatterTest extends TestCase
|
||||
array(1.125, '1.12'),
|
||||
array(1.127, '1.13'),
|
||||
array(1.129, '1.13'),
|
||||
array(1020 / 100, '10.20'),
|
||||
);
|
||||
}
|
||||
|
||||
@ -498,6 +501,7 @@ abstract class AbstractNumberFormatterTest extends TestCase
|
||||
array(-1.123, '-1.12'),
|
||||
array(-1.125, '-1.12'),
|
||||
array(-1.127, '-1.12'),
|
||||
array(1020 / 100, '10.20'),
|
||||
);
|
||||
}
|
||||
|
||||
@ -522,6 +526,7 @@ abstract class AbstractNumberFormatterTest extends TestCase
|
||||
array(-1.123, '-1.13'),
|
||||
array(-1.125, '-1.13'),
|
||||
array(-1.127, '-1.13'),
|
||||
array(1020 / 100, '10.20'),
|
||||
);
|
||||
}
|
||||
|
||||
@ -546,6 +551,7 @@ abstract class AbstractNumberFormatterTest extends TestCase
|
||||
array(-1.123, '-1.12'),
|
||||
array(-1.125, '-1.12'),
|
||||
array(-1.127, '-1.12'),
|
||||
array(1020 / 100, '10.20'),
|
||||
);
|
||||
}
|
||||
|
||||
@ -570,6 +576,7 @@ abstract class AbstractNumberFormatterTest extends TestCase
|
||||
array(-1.123, '-1.13'),
|
||||
array(-1.125, '-1.13'),
|
||||
array(-1.127, '-1.13'),
|
||||
array(1020 / 100, '10.20'),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@ trait BlockingStoreTestTrait
|
||||
* This test is time sensible: the $clockDelay could be adjust.
|
||||
*
|
||||
* @requires extension pcntl
|
||||
* @requires extension posix
|
||||
* @requires function pcntl_sigwaitinfo
|
||||
*/
|
||||
public function testBlockingLocks()
|
||||
|
@ -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 @@ use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
|
||||
|
||||
/**
|
||||
* @author Samuel Roze <samuel.roze@gmail.com>
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
*/
|
||||
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 @@ class SendMessageMiddleware implements MiddlewareInterface, EnvelopeAwareInterfa
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace Symfony\Component\Messenger\Asynchronous\Routing;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\Messenger\Transport\SenderInterface;
|
||||
|
||||
/**
|
||||
* @author Samuel Roze <samuel.roze@gmail.com>
|
||||
@ -19,42 +20,47 @@ use Psr\Container\ContainerInterface;
|
||||
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 $senders;
|
||||
return $senderId ? $this->senderServiceLocator->get($senderId) : null;
|
||||
}
|
||||
|
||||
private function getSenderIds($message): array
|
||||
private function getSenderId($message): ?string
|
||||
{
|
||||
if (isset($this->messageToSenderIdsMapping[\get_class($message)])) {
|
||||
return $this->messageToSenderIdsMapping[\get_class($message)];
|
||||
return self::getValueFromMessageRouting($this->messageToSenderIdMapping, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public static function getValueFromMessageRouting(array $mapping, $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;
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ use Symfony\Component\Messenger\Transport\SenderInterface;
|
||||
|
||||
/**
|
||||
* @author Samuel Roze <samuel.roze@gmail.com>
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ use Symfony\Component\Console\Input\InputArgument;
|
||||
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 @@ class ConsumeMessagesCommand extends Command
|
||||
*/
|
||||
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 @@ EOF
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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 @@ EOF
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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 @@ class DebugCommand extends Command
|
||||
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 <info>%command.name%</info> command displays all messages that can be
|
||||
dispatched using the message bus:
|
||||
dispatched using the message buses:
|
||||
|
||||
<info>php %command.full_name%</info>
|
||||
|
||||
Or for a specific bus only:
|
||||
|
||||
<info>php %command.full_name% command_bus</info>
|
||||
|
||||
EOF
|
||||
)
|
||||
;
|
||||
@ -61,21 +68,33 @@ EOF
|
||||
{
|
||||
$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('<fg=cyan>%s</fg=cyan>', $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('<fg=cyan>%s</fg=cyan>', $message));
|
||||
foreach ($handlers as $handler) {
|
||||
$tableRows[] = array(sprintf(' handled by <info>%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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ use Symfony\Component\DependencyInjection\Definition;
|
||||
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 @@ class MessengerPass implements CompilerPassInterface
|
||||
*/
|
||||
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 @@ class MessengerPass implements CompilerPassInterface
|
||||
|
||||
$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 @@ class MessengerPass implements CompilerPassInterface
|
||||
}
|
||||
|
||||
$priority = $tag['priority'] ?? 0;
|
||||
$handlerBuses = (array) ($tag['bus'] ?? $busIds);
|
||||
|
||||
foreach ($handles as $messageClass => $method) {
|
||||
if (\is_int($messageClass)) {
|
||||
@ -123,38 +131,57 @@ class MessengerPass implements CompilerPassInterface
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,9 +220,8 @@ class MessengerPass implements CompilerPassInterface
|
||||
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));
|
||||
@ -210,8 +236,12 @@ class MessengerPass implements CompilerPassInterface
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -16,7 +16,9 @@ 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\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 @@ class SendMessageMiddlewareTest extends TestCase
|
||||
$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 @@ class SendMessageMiddlewareTest extends TestCase
|
||||
$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 @@ class SendMessageMiddlewareTest extends TestCase
|
||||
$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 @@ class SendMessageMiddlewareTest extends TestCase
|
||||
$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 @@ class SendMessageMiddlewareTest extends TestCase
|
||||
$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 @@ class SendMessageMiddlewareTest extends TestCase
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -28,13 +28,11 @@ class SenderLocatorTest extends TestCase
|
||||
$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 @@ class SenderLocatorTest extends TestCase
|
||||
$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 @@ class SenderLocatorTest extends TestCase
|
||||
$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 @@ class SenderLocatorTest extends TestCase
|
||||
$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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
|
@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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 <maxime.steinhausser@gmail.com>
|
||||
*/
|
||||
class DebugCommandTest extends TestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
putenv('COLUMNS='.(119 + strlen(PHP_EOL)));
|
||||
}
|
||||
|
||||
protected 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(<<<TXT
|
||||
|
||||
Messenger
|
||||
=========
|
||||
|
||||
command_bus
|
||||
-----------
|
||||
|
||||
The following messages can be dispatched:
|
||||
|
||||
---------------------------------------------------------------------------------------
|
||||
Symfony\Component\Messenger\Tests\Fixtures\DummyCommand
|
||||
handled by Symfony\Component\Messenger\Tests\Fixtures\DummyCommandHandler
|
||||
Symfony\Component\Messenger\Tests\Fixtures\MultipleBusesMessage
|
||||
handled by Symfony\Component\Messenger\Tests\Fixtures\MultipleBusesMessageHandler
|
||||
---------------------------------------------------------------------------------------
|
||||
|
||||
query_bus
|
||||
---------
|
||||
|
||||
The following messages can be dispatched:
|
||||
|
||||
---------------------------------------------------------------------------------------
|
||||
Symfony\Component\Messenger\Tests\Fixtures\DummyQuery
|
||||
handled by Symfony\Component\Messenger\Tests\Fixtures\DummyQueryHandler
|
||||
Symfony\Component\Messenger\Tests\Fixtures\MultipleBusesMessage
|
||||
handled by Symfony\Component\Messenger\Tests\Fixtures\MultipleBusesMessageHandler
|
||||
---------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
TXT
|
||||
, $tester->getDisplay(true)
|
||||
);
|
||||
|
||||
$tester->execute(array('bus' => 'query_bus'), array('decorated' => false));
|
||||
|
||||
$this->assertSame(<<<TXT
|
||||
|
||||
Messenger
|
||||
=========
|
||||
|
||||
query_bus
|
||||
---------
|
||||
|
||||
The following messages can be dispatched:
|
||||
|
||||
---------------------------------------------------------------------------------------
|
||||
Symfony\Component\Messenger\Tests\Fixtures\DummyQuery
|
||||
handled by Symfony\Component\Messenger\Tests\Fixtures\DummyQueryHandler
|
||||
Symfony\Component\Messenger\Tests\Fixtures\MultipleBusesMessage
|
||||
handled by Symfony\Component\Messenger\Tests\Fixtures\MultipleBusesMessageHandler
|
||||
---------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
TXT
|
||||
, $tester->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(<<<TXT
|
||||
|
||||
Messenger
|
||||
=========
|
||||
|
||||
command_bus
|
||||
-----------
|
||||
|
||||
[WARNING] No handled message found in bus "command_bus".
|
||||
|
||||
query_bus
|
||||
---------
|
||||
|
||||
[WARNING] No handled message found in bus "query_bus".
|
||||
|
||||
|
||||
TXT
|
||||
, $tester->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));
|
||||
}
|
||||
}
|
@ -14,30 +14,38 @@ namespace Symfony\Component\Messenger\Tests\DependencyInjection;
|
||||
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 @@ class MessengerPassTest extends TestCase
|
||||
|
||||
$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 @@ class MessengerPassTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
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 @@ class MessengerPassTest extends TestCase
|
||||
|
||||
(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 @@ class MessengerPassTest extends TestCase
|
||||
|
||||
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 @@ class MessengerPassTest extends TestCase
|
||||
|
||||
(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 @@ class MessengerPassTest extends TestCase
|
||||
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 @@ class MessengerPassTest extends TestCase
|
||||
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 @@ class MessengerPassTest extends TestCase
|
||||
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);
|
||||
@ -166,24 +236,7 @@ class MessengerPassTest extends TestCase
|
||||
$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(
|
||||
@ -198,7 +251,7 @@ class MessengerPassTest extends TestCase
|
||||
|
||||
(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()
|
||||
@ -343,9 +396,8 @@ class MessengerPassTest extends TestCase
|
||||
{
|
||||
$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);
|
||||
@ -357,8 +409,7 @@ class MessengerPassTest extends TestCase
|
||||
|
||||
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);
|
||||
@ -405,8 +456,7 @@ class MessengerPassTest extends TestCase
|
||||
*/
|
||||
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()),
|
||||
));
|
||||
@ -420,9 +470,8 @@ class MessengerPassTest extends TestCase
|
||||
*/
|
||||
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')),
|
||||
));
|
||||
@ -430,18 +479,58 @@ class MessengerPassTest extends TestCase
|
||||
(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'))
|
||||
;
|
||||
|
||||
|
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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
|
||||
{
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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
|
||||
{
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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
|
||||
{
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
}
|
||||
}
|
38
src/Symfony/Component/Messenger/Transport/ChainSender.php
Normal file
38
src/Symfony/Component/Messenger/Transport/ChainSender.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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 <http://tobion.de>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -162,6 +162,9 @@ class RouteCollection implements \IteratorAggregate, \Countable
|
||||
|
||||
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;
|
||||
|
@ -317,4 +317,17 @@ class RouteCollectionTest extends TestCase
|
||||
$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'));
|
||||
}
|
||||
}
|
||||
|
@ -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->set(self::SESSION_NAMESPACE.'/token_id', 'RESULT');
|
||||
|
||||
$this->session->expects($this->once())
|
||||
->method('start');
|
||||
|
||||
$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->start();
|
||||
$this->session->set(self::SESSION_NAMESPACE.'/token_id', 'RESULT');
|
||||
|
||||
$this->session->expects($this->never())
|
||||
->method('start');
|
||||
|
||||
$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 @@ class SessionTokenStorageTest extends TestCase
|
||||
*/
|
||||
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 @@ class SessionTokenStorageTest extends TestCase
|
||||
*/
|
||||
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'));
|
||||
}
|
||||
|
@ -107,6 +107,7 @@ class PluralizationRules
|
||||
case 'nl':
|
||||
case 'nn':
|
||||
case 'no':
|
||||
case 'oc':
|
||||
case 'om':
|
||||
case 'or':
|
||||
case 'pa':
|
||||
|
@ -87,7 +87,7 @@ class UrlValidator extends ConstraintValidator
|
||||
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'));
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user