Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 438eb32

Browse filesBrowse files
committed
[FrameworkBundle][Mailer] Add a way to configure some email headers from semantic configuration
1 parent 5a74790 commit 438eb32
Copy full SHA for 438eb32

File tree

Expand file treeCollapse file tree

14 files changed

+301
-34
lines changed
Filter options
Expand file treeCollapse file tree

14 files changed

+301
-34
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+21Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1492,6 +1492,7 @@ private function addMailerSection(ArrayNodeDefinition $rootNode)
14921492
->thenInvalid('"dsn" and "transports" cannot be used together.')
14931493
->end()
14941494
->fixXmlConfig('transport')
1495+
->fixXmlConfig('header')
14951496
->children()
14961497
->scalarNode('message_bus')->defaultNull()->info('The message bus to use. Defaults to the default bus if the Messenger component is installed.')->end()
14971498
->scalarNode('dsn')->defaultNull()->end()
@@ -1515,6 +1516,26 @@ private function addMailerSection(ArrayNodeDefinition $rootNode)
15151516
->end()
15161517
->end()
15171518
->end()
1519+
->arrayNode('headers')
1520+
->normalizeKeys(false)
1521+
->useAttributeAsKey('key')
1522+
->prototype('array')
1523+
->normalizeKeys(false)
1524+
->beforeNormalization()
1525+
->ifTrue(function ($v) {
1526+
if (\is_array($v)) {
1527+
return array_keys($v) !== ['value'];
1528+
}
1529+
1530+
return true;
1531+
})
1532+
->then(function ($v) { return ['value' => $v]; })
1533+
->end()
1534+
->children()
1535+
->variableNode('value')->end()
1536+
->end()
1537+
->end()
1538+
->end()
15181539
->end()
15191540
->end()
15201541
->end()

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+18-5Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
use Symfony\Component\Messenger\MessageBusInterface;
9090
use Symfony\Component\Messenger\Transport\TransportFactoryInterface;
9191
use Symfony\Component\Messenger\Transport\TransportInterface;
92+
use Symfony\Component\Mime\Header\Headers;
9293
use Symfony\Component\Mime\MimeTypeGuesserInterface;
9394
use Symfony\Component\Mime\MimeTypes;
9495
use Symfony\Component\Notifier\Bridge\Firebase\FirebaseTransportFactory;
@@ -1986,12 +1987,24 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co
19861987
}
19871988
}
19881989

1989-
$recipients = $config['envelope']['recipients'] ?? null;
1990-
$sender = $config['envelope']['sender'] ?? null;
1991-
19921990
$envelopeListener = $container->getDefinition('mailer.envelope_listener');
1993-
$envelopeListener->setArgument(0, $sender);
1994-
$envelopeListener->setArgument(1, $recipients);
1991+
$envelopeListener->setArgument(0, $config['envelope']['sender'] ?? null);
1992+
$envelopeListener->setArgument(1, $config['envelope']['recipients'] ?? null);
1993+
1994+
if ($config['headers']) {
1995+
$headers = new Headers();
1996+
foreach ($config['headers'] as $name => $data) {
1997+
$value = $data['value'];
1998+
if (\in_array($name, ['from', 'to', 'cc', 'bcc', 'reply-to'])) {
1999+
$value = (array) $value;
2000+
}
2001+
$headers->addHeader($name, $value);
2002+
}
2003+
$messageListener = $container->getDefinition('mailer.message_listener');
2004+
$messageListener->setArgument(0, $headers);
2005+
} else {
2006+
$container->removeDefinition('mailer.message_listener');
2007+
}
19952008
}
19962009

19972010
private function registerNotifierConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)

‎src/Symfony/Bundle/FrameworkBundle/Resources/config/mailer.xml

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Resources/config/mailer.xml
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@
3939
<tag name="kernel.event_subscriber"/>
4040
</service>
4141

42+
<service id="mailer.message_listener" class="Symfony\Component\Mailer\EventListener\MessageListener">
43+
<argument /> <!-- headers -->
44+
<tag name="kernel.event_subscriber"/>
45+
</service>
46+
4247
<service id="mailer.logger_message_listener" class="Symfony\Component\Mailer\EventListener\MessageLoggerListener">
4348
<tag name="kernel.event_subscriber"/>
4449
</service>

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
+6-1Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -560,14 +560,19 @@
560560
<xsd:complexType name="mailer">
561561
<xsd:sequence>
562562
<xsd:element name="envelope" type="mailer_envelope" minOccurs="0" maxOccurs="1" />
563+
<xsd:element name="header" type="header" minOccurs="0" maxOccurs="unbounded" />
563564
</xsd:sequence>
564565
<xsd:attribute name="dsn" type="xsd:string" />
565566
<xsd:attribute name="message-bus" type="xsd:string" />
566567
</xsd:complexType>
567568

569+
<xsd:complexType name="header" mixed="true">
570+
<xsd:attribute name="key" type="xsd:string" use="required" />
571+
</xsd:complexType>
572+
568573
<xsd:complexType name="mailer_envelope">
569574
<xsd:sequence>
570-
<xsd:element name="sender" type="xsd:string" />
575+
<xsd:element name="sender" type="xsd:string" minOccurs="0" maxOccurs="1" />
571576
<xsd:element name="recipients" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
572577
</xsd:sequence>
573578
</xsd:complexType>

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,7 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor
493493
'transports' => [],
494494
'enabled' => !class_exists(FullStack::class) && class_exists(Mailer::class),
495495
'message_bus' => null,
496+
'headers' => [],
496497
],
497498
'notifier' => [
498499
'enabled' => !class_exists(FullStack::class) && class_exists(Notifier::class),

‎src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/mailer.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/mailer.php
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,10 @@
77
'sender' => 'sender@example.org',
88
'recipients' => ['redirected@example.org', 'redirected1@example.org'],
99
],
10+
'headers' => [
11+
'from' => 'from@example.org',
12+
'bcc' => ['bcc1@example.org', 'bcc2@example.org'],
13+
'foo' => 'bar',
14+
],
1015
],
1116
]);

‎src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/mailer.xml

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/mailer.xml
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
<framework:recipients>redirected@example.org</framework:recipients>
1414
<framework:recipients>redirected1@example.org</framework:recipients>
1515
</framework:envelope>
16+
<framework:header key="from">from@example.org</framework:header>
17+
<framework:header key="bcc">bcc1@example.org</framework:header>
18+
<framework:header key="foo">bar</framework:header>
1619
</framework:mailer>
1720
</framework:config>
1821
</container>

‎src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/mailer.yml

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/mailer.yml
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,7 @@ framework:
66
recipients:
77
- redirected@example.org
88
- redirected1@example.org
9+
headers:
10+
from: from@example.org
11+
bcc: [bcc1@example.org, bcc2@example.org]
12+
foo: bar

‎src/Symfony/Bundle/FrameworkBundle/composer.json

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/composer.json
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
"symfony/expression-language": "^4.4|^5.0",
4646
"symfony/http-client": "^4.4|^5.0",
4747
"symfony/lock": "^4.4|^5.0",
48-
"symfony/mailer": "^4.4|^5.0",
48+
"symfony/mailer": "^5.2",
4949
"symfony/messenger": "^4.4|^5.0",
5050
"symfony/mime": "^4.4|^5.0",
5151
"symfony/process": "^4.4|^5.0",
@@ -79,7 +79,7 @@
7979
"symfony/http-client": "<4.4",
8080
"symfony/form": "<4.4",
8181
"symfony/lock": "<4.4",
82-
"symfony/mailer": "<4.4",
82+
"symfony/mailer": "<5.2",
8383
"symfony/messenger": "<4.4",
8484
"symfony/mime": "<4.4",
8585
"symfony/property-info": "<4.4",

‎src/Symfony/Component/Mailer/EventListener/MessageListener.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/EventListener/MessageListener.php
+59-7Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@
1313

1414
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
1515
use Symfony\Component\Mailer\Event\MessageEvent;
16+
use Symfony\Component\Mailer\Exception\InvalidArgumentException;
17+
use Symfony\Component\Mailer\Exception\RuntimeException;
1618
use Symfony\Component\Mime\BodyRendererInterface;
1719
use Symfony\Component\Mime\Header\Headers;
20+
use Symfony\Component\Mime\Header\MailboxListHeader;
1821
use Symfony\Component\Mime\Message;
1922

2023
/**
@@ -24,13 +27,38 @@
2427
*/
2528
class MessageListener implements EventSubscriberInterface
2629
{
30+
public const HEADER_SET_IF_EMPTY = 1;
31+
public const HEADER_ADD = 2;
32+
public const HEADER_REPLACE = 3;
33+
public const DEFAULT_RULES = [
34+
'from' => self::HEADER_SET_IF_EMPTY,
35+
'return-path' => self::HEADER_SET_IF_EMPTY,
36+
'reply-to' => self::HEADER_ADD,
37+
'to' => self::HEADER_SET_IF_EMPTY,
38+
'cc' => self::HEADER_ADD,
39+
'bcc' => self::HEADER_ADD,
40+
];
41+
2742
private $headers;
43+
private $headerRules = [];
2844
private $renderer;
2945

30-
public function __construct(Headers $headers = null, BodyRendererInterface $renderer = null)
46+
public function __construct(Headers $headers = null, BodyRendererInterface $renderer = null, array $headerRules = self::DEFAULT_RULES)
3147
{
3248
$this->headers = $headers;
3349
$this->renderer = $renderer;
50+
foreach ($headerRules as $headerName => $rule) {
51+
$this->addHeaderRule($headerName, $rule);
52+
}
53+
}
54+
55+
public function addHeaderRule(string $headerName, int $rule)
56+
{
57+
if ($rule < 1 || $rule > 3) {
58+
throw new InvalidArgumentException(sprintf('The "%d" rule is not supported.', $rule));
59+
}
60+
61+
$this->headerRules[$headerName] = $rule;
3462
}
3563

3664
public function onMessage(MessageEvent $event): void
@@ -54,14 +82,38 @@ private function setHeaders(Message $message): void
5482
foreach ($this->headers->all() as $name => $header) {
5583
if (!$headers->has($name)) {
5684
$headers->add($header);
57-
} else {
58-
if (Headers::isUniqueHeader($name)) {
59-
continue;
60-
}
61-
$headers->add($header);
85+
86+
continue;
87+
}
88+
89+
switch ($this->headerRules[$name] ?? self::HEADER_SET_IF_EMPTY) {
90+
case self::HEADER_SET_IF_EMPTY:
91+
break;
92+
93+
case self::HEADER_REPLACE:
94+
$headers->remove($name);
95+
$headers->add($header);
96+
97+
break;
98+
99+
case self::HEADER_ADD:
100+
if (!Headers::isUniqueHeader($name)) {
101+
$headers->add($header);
102+
103+
break;
104+
}
105+
106+
$h = $headers->get($name);
107+
if (!$h instanceof MailboxListHeader) {
108+
throw new RuntimeException(sprintf('Unable to set header "%s".', $name));
109+
}
110+
111+
Headers::checkHeaderClass($header);
112+
foreach ($header->getAddresses() as $address) {
113+
$h->addAddress($address);
114+
}
62115
}
63116
}
64-
$message->setHeaders($headers);
65117
}
66118

67119
private function renderMessage(Message $message): void
+105Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
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\Mailer\Tests;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Mailer\Envelope;
16+
use Symfony\Component\Mailer\Event\MessageEvent;
17+
use Symfony\Component\Mailer\EventListener\MessageListener;
18+
use Symfony\Component\Mime\Address;
19+
use Symfony\Component\Mime\Header\Headers;
20+
use Symfony\Component\Mime\Header\MailboxListHeader;
21+
use Symfony\Component\Mime\Header\UnstructuredHeader;
22+
use Symfony\Component\Mime\Message;
23+
24+
class MessageListenerTest extends TestCase
25+
{
26+
/**
27+
* @dataProvider provideHeaders
28+
*/
29+
public function testHeaders(Headers $initialHeaders, Headers $defaultHeaders, Headers $expectedHeaders, array $rules = MessageListener::DEFAULT_RULES)
30+
{
31+
$message = new Message($initialHeaders);
32+
$listener = new MessageListener($defaultHeaders, null, $rules);
33+
$event = new MessageEvent($message, new Envelope(new Address('sender@example.com'), [new Address('recipient@example.com')]), 'smtp');
34+
$listener->onMessage($event);
35+
36+
$this->assertEquals($expectedHeaders, $event->getMessage()->getHeaders());
37+
}
38+
39+
public function provideHeaders(): iterable
40+
{
41+
$initialHeaders = new Headers();
42+
$defaultHeaders = (new Headers())
43+
->add(new MailboxListHeader('from', [new Address('from-default@example.com')]))
44+
;
45+
yield 'No defaults, all headers copied over' => [$initialHeaders, $defaultHeaders, $defaultHeaders];
46+
47+
$initialHeaders = new Headers();
48+
$defaultHeaders = (new Headers())
49+
->add(new UnstructuredHeader('foo', 'bar'))
50+
->add(new UnstructuredHeader('bar', 'foo'))
51+
;
52+
yield 'No defaults, default is to set if empty' => [$initialHeaders, $defaultHeaders, $defaultHeaders];
53+
54+
$initialHeaders = (new Headers())
55+
->add(new UnstructuredHeader('foo', 'initial'))
56+
;
57+
$defaultHeaders = (new Headers())
58+
->add(new UnstructuredHeader('foo', 'bar'))
59+
->add(new UnstructuredHeader('bar', 'foo'))
60+
;
61+
$expectedHeaders = (new Headers())
62+
->add(new UnstructuredHeader('foo', 'initial'))
63+
->add(new UnstructuredHeader('bar', 'foo'))
64+
;
65+
yield 'Some defaults, default is to set if empty' => [$initialHeaders, $defaultHeaders, $expectedHeaders];
66+
67+
$initialHeaders = (new Headers())
68+
->add(new UnstructuredHeader('foo', 'initial'))
69+
;
70+
$defaultHeaders = (new Headers())
71+
->add(new UnstructuredHeader('foo', 'bar'))
72+
->add(new UnstructuredHeader('bar', 'foo'))
73+
;
74+
$rules = [
75+
'foo' => MessageListener::HEADER_REPLACE,
76+
];
77+
yield 'Some defaults, replace if set' => [$initialHeaders, $defaultHeaders, $defaultHeaders, $rules];
78+
79+
$initialHeaders = (new Headers())
80+
->add(new UnstructuredHeader('foo', 'bar'))
81+
;
82+
$defaultHeaders = (new Headers())
83+
->add(new UnstructuredHeader('foo', 'foo'))
84+
;
85+
$expectedHeaders = (new Headers())
86+
->add(new UnstructuredHeader('foo', 'bar'))
87+
->add(new UnstructuredHeader('foo', 'foo'))
88+
;
89+
$rules = [
90+
'foo' => MessageListener::HEADER_ADD,
91+
];
92+
yield 'Some defaults, add if set (not unique header)' => [$initialHeaders, $defaultHeaders, $expectedHeaders, $rules];
93+
94+
$initialHeaders = (new Headers())
95+
->add(new MailboxListHeader('bcc', [new Address('bcc-initial@example.com')]))
96+
;
97+
$defaultHeaders = (new Headers())
98+
->add(new MailboxListHeader('bcc', [new Address('bcc-default@example.com'), new Address('bcc-default-1@example.com')]))
99+
;
100+
$expectedHeaders = (new Headers())
101+
->add(new MailboxListHeader('bcc', [new Address('bcc-initial@example.com'), new Address('bcc-default@example.com'), new Address('bcc-default-1@example.com')]))
102+
;
103+
yield 'bcc, add another bcc (unique header)' => [$initialHeaders, $defaultHeaders, $expectedHeaders];
104+
}
105+
}

‎src/Symfony/Component/Mailer/composer.json

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/composer.json
+3-2Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"egulias/email-validator": "^2.1.10",
2121
"psr/log": "~1.0",
2222
"symfony/event-dispatcher": "^4.4|^5.0",
23-
"symfony/mime": "^4.4|^5.0",
23+
"symfony/mime": "^5.2",
2424
"symfony/polyfill-php80": "^1.15",
2525
"symfony/service-contracts": "^1.1|^2"
2626
},
@@ -35,7 +35,8 @@
3535
"symfony/sendgrid-mailer": "^4.4|^5.0"
3636
},
3737
"conflict": {
38-
"symfony/http-kernel": "<4.4"
38+
"symfony/http-kernel": "<4.4",
39+
"symfony/mime": "<5.2"
3940
},
4041
"autoload": {
4142
"psr-4": { "Symfony\\Component\\Mailer\\": "" },

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.