Skip to content

Commit ef04c78

Browse files
committed
[Mailer] Add recipient_fetcher option that allows fetching global recipients from a service
1 parent 929e82e commit ef04c78

20 files changed

+378
-11
lines changed

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ CHANGELOG
2020
* Initialize `router.request_context`'s `_locale` parameter to `%kernel.default_locale%`
2121
* Deprecate `ConfigBuilderCacheWarmer`, return PHP arrays from your config instead
2222
* Add support for selecting the appropriate error renderer based on the `APP_RUNTIME_MODE` env var
23+
* Add `mailer.envelope.recipient_fetcher` option that allows fetching global recipients from a service
2324

2425
7.3
2526
---

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
use Symfony\Component\JsonStreamer\StreamWriterInterface;
3636
use Symfony\Component\Lock\Lock;
3737
use Symfony\Component\Lock\Store\SemaphoreStore;
38+
use Symfony\Component\Mailer\Envelope\RecipientFetcherInterface;
3839
use Symfony\Component\Mailer\Mailer;
3940
use Symfony\Component\Messenger\MessageBusInterface;
4041
use Symfony\Component\Notifier\Notifier;
@@ -2347,6 +2348,20 @@ private function addMailerSection(ArrayNodeDefinition $rootNode, callable $enabl
23472348
->end()
23482349
->prototype('scalar')->end()
23492350
->end()
2351+
->scalarNode('recipient_fetcher')
2352+
->info('A service that fetch the recipients.')
2353+
->defaultNull()
2354+
->validate()
2355+
->ifFalse(static function ($v) {
2356+
if (!$v) {
2357+
return true;
2358+
}
2359+
2360+
return is_a($v, RecipientFetcherInterface::class, true);
2361+
})
2362+
->thenInvalid('The %s class must implement the "'.RecipientFetcherInterface::class.'" interface.')
2363+
->end()
2364+
->end()
23502365
->arrayNode('allowed_recipients', 'allowed_recipient')
23512366
->info('A list of regular expressions that allow recipients when "recipients" option is defined.')
23522367
->example(['.*@example\.com'])

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@
126126
use Symfony\Component\Lock\Store\StoreFactory;
127127
use Symfony\Component\Mailer\Bridge as MailerBridge;
128128
use Symfony\Component\Mailer\Command\MailerTestCommand;
129+
use Symfony\Component\Mailer\Envelope\RecipientFetcherInterface;
129130
use Symfony\Component\Mailer\EventListener\DkimSignedMessageListener;
130131
use Symfony\Component\Mailer\EventListener\MessengerTransportListener;
131132
use Symfony\Component\Mailer\EventListener\SmimeEncryptedMessageListener;
@@ -3016,6 +3017,8 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co
30163017
throw new LogicException('Mailer support cannot be enabled as the component is not installed. Try running "composer require symfony/mailer".');
30173018
}
30183019

3020+
$container->registerForAutoconfiguration(RecipientFetcherInterface::class);
3021+
30193022
$loader->load('mailer.php');
30203023
$loader->load('mailer_transports.php');
30213024
if (!\count($config['transports']) && null === $config['dsn']) {
@@ -3089,7 +3092,13 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co
30893092

30903093
$envelopeListener = $container->getDefinition('mailer.envelope_listener');
30913094
$envelopeListener->setArgument(0, $config['envelope']['sender'] ?? null);
3092-
$envelopeListener->setArgument(1, $config['envelope']['recipients'] ?? null);
3095+
3096+
$recipients = $config['envelope']['recipients'] ?? null;
3097+
if (isset($config['envelope']['recipient_fetcher'])) {
3098+
$recipients = new Reference($config['envelope']['recipient_fetcher']);
3099+
}
3100+
3101+
$envelopeListener->setArgument(1, $recipients);
30933102
$envelopeListener->setArgument(2, $config['envelope']['allowed_recipients'] ?? []);
30943103

30953104
if ($config['headers']) {

src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -870,6 +870,7 @@
870870
<xsd:sequence>
871871
<xsd:element name="sender" type="xsd:string" minOccurs="0" maxOccurs="1" />
872872
<xsd:element name="recipient" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
873+
<xsd:element name="recipient-fetcher" type="xsd:string" minOccurs="0" maxOccurs="1" />
873874
<xsd:element name="allowed-recipient" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
874875
</xsd:sequence>
875876
</xsd:complexType>

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use PHPUnit\Framework\Attributes\TestWith;
1717
use PHPUnit\Framework\TestCase;
1818
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Configuration;
19+
use Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Mailer\InvalidRecipientFetcher;
1920
use Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Places;
2021
use Symfony\Bundle\FullStack;
2122
use Symfony\Component\AssetMapper\Compressor\CompressorInterface;
@@ -420,6 +421,32 @@ public static function provideValidSemaphoreConfigurationTests()
420421
yield [['enabled' => false, 'resource' => [['name' => 'foo', 'value' => 'redis://foo'], ['name' => 'bar', 'value' => 'redis://bar']]], ['enabled' => false, 'resources' => ['foo' => 'redis://foo', 'bar' => 'redis://bar']]];
421422
}
422423

424+
#[DataProvider('provideInvalidMailerRecipientFetchers')]
425+
public function testInvalidMailerRecipientFetcher(string $class, string $recipientFetcher): void
426+
{
427+
$this->expectException(InvalidConfigurationException::class);
428+
$this->expectExceptionMessage(\sprintf('Invalid configuration for path "framework.mailer.envelope.recipient_fetcher": The "%s" class must implement the "Symfony\Component\Mailer\Envelope\RecipientFetcherInterface" interface.', $class));
429+
430+
$processor = new Processor();
431+
$configuration = new Configuration(true);
432+
433+
$processor->processConfiguration($configuration, [
434+
'framework' => [
435+
'mailer' => [
436+
'envelope' => [
437+
'recipient_fetcher' => $recipientFetcher,
438+
],
439+
],
440+
],
441+
]);
442+
}
443+
444+
public static function provideInvalidMailerRecipientFetchers(): iterable
445+
{
446+
yield ['invalid', 'invalid'];
447+
yield ['Symfony\\\Bundle\\\FrameworkBundle\\\Tests\\\DependencyInjection\\\Fixtures\\\Mailer\\\InvalidRecipientFetcher', InvalidRecipientFetcher::class];
448+
}
449+
423450
public function testItShowANiceMessageIfTwoMessengerBusesAreConfiguredButNoDefaultBus()
424451
{
425452
$expectedMessage = 'You must specify the "default_bus" if you define more than one bus.';
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Mailer;
13+
14+
class InvalidRecipientFetcher
15+
{
16+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Mailer;
13+
14+
use Symfony\Component\Mailer\Envelope\RecipientFetcherInterface;
15+
use Symfony\Component\Mime\Address;
16+
17+
class RecipientFetcher implements RecipientFetcherInterface
18+
{
19+
/**
20+
* @return array<Address|string>
21+
*/
22+
public function fetchRecipients(): array
23+
{
24+
return [];
25+
}
26+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
13+
14+
use Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Mailer\RecipientFetcher;
15+
16+
return static function (ContainerConfigurator $container) {
17+
$container->services()
18+
->set(RecipientFetcher::class)
19+
->public();
20+
21+
$container->extension('framework', [
22+
'annotations' => false,
23+
'http_method_override' => false,
24+
'handle_all_throwables' => true,
25+
'php_errors' => ['log' => true],
26+
'mailer' => [
27+
'dsn' => 'smtp://example.com',
28+
'envelope' => [
29+
'sender' => 'sender@example.org',
30+
'recipient_fetcher' => RecipientFetcher::class,
31+
],
32+
'headers' => [
33+
'from' => 'from@example.org',
34+
'bcc' => ['bcc1@example.org', 'bcc2@example.org'],
35+
'foo' => 'bar',
36+
],
37+
],
38+
]);
39+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
13+
14+
use Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Mailer\RecipientFetcher;
15+
16+
return static function (ContainerConfigurator $container) {
17+
$container->services()
18+
->set(RecipientFetcher::class)
19+
->public();
20+
21+
$container->extension('framework', [
22+
'annotations' => false,
23+
'http_method_override' => false,
24+
'handle_all_throwables' => true,
25+
'php_errors' => ['log' => true],
26+
'mailer' => [
27+
'dsn' => 'smtp://example.com',
28+
'envelope' => [
29+
'sender' => 'sender@example.org',
30+
'recipients' => ['redirected@example.org'],
31+
'recipient_fetcher' => RecipientFetcher::class,
32+
],
33+
'headers' => [
34+
'from' => 'from@example.org',
35+
'bcc' => ['bcc1@example.org', 'bcc2@example.org'],
36+
'foo' => 'bar',
37+
],
38+
],
39+
]);
40+
};
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" ?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xmlns:framework="http://symfony.com/schema/dic/symfony"
6+
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
7+
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
8+
9+
<services>
10+
<service id="Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Mailer\RecipientFetcher" public="true"/>
11+
</services>
12+
13+
<framework:config http-method-override="false" handle-all-throwables="true">
14+
<framework:annotations enabled="false" />
15+
<framework:php-errors log="true" />
16+
<framework:mailer dsn="smtp://example.com">
17+
<framework:envelope>
18+
<framework:sender>sender@example.org</framework:sender>
19+
<framework:recipient-fetcher>Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Mailer\RecipientFetcher</framework:recipient-fetcher>
20+
</framework:envelope>
21+
<framework:header name="from">from@example.org</framework:header>
22+
<framework:header name="bcc">bcc1@example.org</framework:header>
23+
<framework:header name="foo">bar</framework:header>
24+
</framework:mailer>
25+
</framework:config>
26+
</container>

0 commit comments

Comments
 (0)