Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 51f739e

Browse files
committed
added the Mailer component
1 parent 162d5a8 commit 51f739e

File tree

97 files changed

+4886
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+4886
-0
lines changed

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use Symfony\Component\HttpFoundation\Cookie;
2727
use Symfony\Component\Lock\Lock;
2828
use Symfony\Component\Lock\Store\SemaphoreStore;
29+
use Symfony\Component\Mailer\Mailer;
2930
use Symfony\Component\Messenger\MessageBusInterface;
3031
use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface;
3132
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
@@ -112,6 +113,7 @@ public function getConfigTreeBuilder()
112113
$this->addMessengerSection($rootNode);
113114
$this->addRobotsIndexSection($rootNode);
114115
$this->addHttpClientSection($rootNode);
116+
$this->addMailerSection($rootNode);
115117

116118
return $treeBuilder;
117119
}
@@ -1344,4 +1346,19 @@ private function addHttpClientOptionsSection(NodeBuilder $rootNode)
13441346
->end()
13451347
;
13461348
}
1349+
1350+
private function addMailerSection(ArrayNodeDefinition $rootNode)
1351+
{
1352+
$rootNode
1353+
->children()
1354+
->arrayNode('mailer')
1355+
->info('Mailer configuration')
1356+
->{!class_exists(FullStack::class) && class_exists(Mailer::class) ? 'canBeDisabled' : 'canBeEnabled'}()
1357+
->children()
1358+
->scalarNode('dsn')->defaultValue('smtp://null')->end()
1359+
->end()
1360+
->end()
1361+
->end()
1362+
;
1363+
}
13471364
}

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
use Symfony\Component\Lock\Store\FlockStore;
7575
use Symfony\Component\Lock\Store\StoreFactory;
7676
use Symfony\Component\Lock\StoreInterface;
77+
use Symfony\Component\Mailer\Mailer;
7778
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
7879
use Symfony\Component\Messenger\MessageBus;
7980
use Symfony\Component\Messenger\MessageBusInterface;
@@ -316,6 +317,10 @@ public function load(array $configs, ContainerBuilder $container)
316317
$this->registerHttpClientConfiguration($config['http_client'], $container, $loader);
317318
}
318319

320+
if ($this->isConfigEnabled($container, $config['mailer'])) {
321+
$this->registerMailerConfiguration($config['mailer'], $container, $loader);
322+
}
323+
319324
if ($this->isConfigEnabled($container, $config['web_link'])) {
320325
if (!class_exists(HttpHeaderSerializer::class)) {
321326
throw new LogicException('WebLink support cannot be enabled as the WebLink component is not installed. Try running "composer require symfony/weblink".');
@@ -1854,6 +1859,16 @@ public function merge(array $options, array $defaultOptions)
18541859
}
18551860
}
18561861

1862+
private function registerMailerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
1863+
{
1864+
if (!class_exists(Mailer::class)) {
1865+
throw new LogicException('Mailer support cannot be enabled as the component is not installed. Try running "composer require symfony/mailer".');
1866+
}
1867+
1868+
$loader->load('mailer.xml');
1869+
$container->getDefinition('mailer.transport')->setArgument(0, $config['dsn']);
1870+
}
1871+
18571872
/**
18581873
* Returns the base path for the XSD files.
18591874
*
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
6+
7+
<services>
8+
<defaults public="false" />
9+
10+
<service id="mailer" class="Symfony\Component\Mailer\Mailer">
11+
<argument type="service" id="mailer.transport" />
12+
<argument type="service" id="message_bus" on-invalid="ignore" />
13+
</service>
14+
<service id="Symfony\Component\Mailer\MailerInterface" alias="mailer" />
15+
16+
<service id="mailer.transport" class="Symfony\Component\Mailer\Transport\TransportInterface">
17+
<factory class="Symfony\Component\Mailer\Transport" method="fromDsn" />
18+
<argument></argument> <!-- env(MAILER_DSN) -->
19+
<argument type="service" id="event_dispatcher" />
20+
<argument type="service" id="http_client" on-invalid="ignore" />
21+
<argument type="service" id="logger" on-invalid="ignore" />
22+
</service>
23+
<service id="Symfony\Component\Mailer\Transport\TransportInterface" alias="mailer.transport" />
24+
25+
<service id="mailer.messenger.message_handler" class="Symfony\Component\Mailer\Messenger\MessageHandler">
26+
<argument type="service" id="mailer.transport" />
27+
<tag name="messenger.message_handler" />
28+
</service>
29+
</services>
30+
</container>

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,10 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor
336336
'enabled' => !class_exists(FullStack::class) && class_exists(HttpClient::class),
337337
'clients' => [],
338338
],
339+
'mailer' => [
340+
'dsn' => 'smtp://null',
341+
'enabled' => !class_exists(FullStack::class) && class_exists(HttpClient::class),
342+
],
339343
];
340344
}
341345
}

src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use Twig\Extension\ExtensionInterface;
2424
use Twig\Extension\RuntimeExtensionInterface;
2525
use Twig\Loader\LoaderInterface;
26+
use Symfony\Component\Mailer\Mailer;
2627

2728
/**
2829
* TwigExtension.
@@ -49,6 +50,10 @@ public function load(array $configs, ContainerBuilder $container)
4950
$loader->load('console.xml');
5051
}
5152

53+
if (class_exists(Mailer::class)) {
54+
$loader->load('mailer.xml');
55+
}
56+
5257
if (!class_exists(Translator::class)) {
5358
$container->removeDefinition('twig.translation.extractor');
5459
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
6+
7+
<services>
8+
<defaults public="false" />
9+
10+
<service id="twig.mailer.message_listener" class="Symfony\Component\Mailer\EventListener\MessageListener">
11+
<argument />
12+
<argument type="service" id="twig.mime_body_renderer" />
13+
</service>
14+
15+
<service id="twig.mime_body_renderer" class="Symfony\Bridge\Twig\Mime\BodyRenderer">
16+
<argument type="service" id="twig" />
17+
</service>
18+
</services>
19+
</container>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CHANGELOG
2+
=========
3+
4+
4.3.0
5+
-----
6+
7+
* added the bridge
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Bridge\Amazon\Http\Api;
13+
14+
use Psr\Log\LoggerInterface;
15+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
16+
use Symfony\Component\Mailer\Exception\TransportException;
17+
use Symfony\Component\Mailer\SmtpEnvelope;
18+
use Symfony\Component\Mailer\Transport\Http\Api\AbstractApiTransport;
19+
use Symfony\Component\Mime\Email;
20+
use Symfony\Contracts\HttpClient\HttpClientInterface;
21+
22+
/**
23+
* @experimental in 4.3
24+
*/
25+
class SesTransport extends AbstractApiTransport
26+
{
27+
private const ENDPOINT = 'https://email.%region%.amazonaws.com';
28+
29+
private $accessKey;
30+
private $secretKey;
31+
private $region;
32+
33+
/**
34+
* @param string $region Amazon SES region (currently one of us-east-1, us-west-2, or eu-west-1)
35+
*/
36+
public function __construct(string $accessKey, string $secretKey, string $region = null, HttpClientInterface $client = null, EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null)
37+
{
38+
$this->accessKey = $accessKey;
39+
$this->secretKey = $secretKey;
40+
$this->region = $region ?: 'eu-west-1';
41+
42+
parent::__construct($client, $dispatcher, $logger);
43+
}
44+
45+
protected function doSendEmail(Email $email, SmtpEnvelope $envelope): void
46+
{
47+
$date = gmdate('D, d M Y H:i:s e');
48+
$auth = sprintf('AWS3-HTTPS AWSAccessKeyId=%s,Algorithm=HmacSHA256,Signature=%s', $this->accessKey, $this->getSignature($date));
49+
50+
$endpoint = str_replace('%region%', $this->region, self::ENDPOINT);
51+
$response = $this->client->request('POST', $endpoint, [
52+
'headers' => [
53+
'X-Amzn-Authorization' => $auth,
54+
'Date' => $date,
55+
'Content-Type' => 'application/x-www-form-urlencoded',
56+
],
57+
'body' => $this->getPayload($email, $envelope),
58+
]);
59+
60+
if (200 !== $response->getStatusCode()) {
61+
$error = new \SimpleXMLElement($response->getContent(false));
62+
63+
throw new TransportException(sprintf('Unable to send an email: %s (code %s).', $error->Error->Message, $error->Error->Code));
64+
}
65+
}
66+
67+
private function getSignature(string $string): string
68+
{
69+
return base64_encode(hash_hmac('sha256', $string, $this->secretKey, true));
70+
}
71+
72+
private function getPayload(Email $email, SmtpEnvelope $envelope): array
73+
{
74+
if ($email->getAttachments()) {
75+
return [
76+
'Action' => 'SendRawEmail',
77+
'RawMessage.Data' => \base64_encode($email->toString()),
78+
];
79+
}
80+
81+
$payload = [
82+
'Action' => 'SendEmail',
83+
'Destination.ToAddresses.member' => $this->stringifyAddresses($this->getRecipients($email, $envelope)),
84+
'Message.Subject.Data' => $email->getSubject(),
85+
'Source' => $envelope->getSender()->toString(),
86+
];
87+
88+
if ($emails = $email->getCc()) {
89+
$payload['Destination.CcAddresses.member'] = $this->stringifyAddresses($emails);
90+
}
91+
if ($emails = $email->getBcc()) {
92+
$payload['Destination.BccAddresses.member'] = $this->stringifyAddresses($emails);
93+
}
94+
if ($email->getTextBody()) {
95+
$payload['Message.Body.Text.Data'] = $email->getTextBody();
96+
}
97+
if ($email->getHtmlBody()) {
98+
$payload['Message.Body.Html.Data'] = $email->getHtmlBody();
99+
}
100+
101+
return $payload;
102+
}
103+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Bridge\Amazon\Http;
13+
14+
use Psr\Log\LoggerInterface;
15+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
16+
use Symfony\Component\HttpClient\HttpClient;
17+
use Symfony\Component\Mailer\Exception\TransportException;
18+
use Symfony\Component\Mailer\SentMessage;
19+
use Symfony\Component\Mailer\Transport\AbstractTransport;
20+
use Symfony\Contracts\HttpClient\HttpClientInterface;
21+
22+
/**
23+
* @experimental in 4.3
24+
*/
25+
class SesTransport extends AbstractTransport
26+
{
27+
private const ENDPOINT = 'https://email.%region%.amazonaws.com';
28+
29+
private $client;
30+
private $accessKey;
31+
private $secretKey;
32+
private $region;
33+
34+
/**
35+
* @param string $region Amazon SES region (currently one of us-east-1, us-west-2, or eu-west-1)
36+
*/
37+
public function __construct(string $accessKey, string $secretKey, string $region = null, HttpClientInterface $client = null, EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null)
38+
{
39+
$this->client = $client ?? HttpClient::create();
40+
$this->accessKey = $accessKey;
41+
$this->secretKey = $secretKey;
42+
$this->region = $region ?: 'eu-west-1';
43+
44+
parent::__construct($dispatcher, $logger);
45+
}
46+
47+
protected function doSend(SentMessage $message): void
48+
{
49+
$date = gmdate('D, d M Y H:i:s e');
50+
$auth = sprintf('AWS3-HTTPS AWSAccessKeyId=%s,Algorithm=HmacSHA256,Signature=%s', $this->accessKey, $this->getSignature($date));
51+
52+
$endpoint = str_replace('%region%', $this->region, self::ENDPOINT);
53+
$response = $this->client->request('POST', $endpoint, [
54+
'headers' => [
55+
'X-Amzn-Authorization' => $auth,
56+
'Date' => $date,
57+
],
58+
'body' => [
59+
'Action' => 'SendRawEmail',
60+
'RawMessage.Data' => \base64_encode($message->toString()),
61+
],
62+
]);
63+
64+
if (200 !== $response->getStatusCode()) {
65+
$error = new \SimpleXMLElement($response->getContent(false));
66+
67+
throw new TransportException(sprintf('Unable to send an email: %s (code %s).', $error->Error->Message, $error->Error->Code));
68+
}
69+
}
70+
71+
private function getSignature(string $string): string
72+
{
73+
return base64_encode(hash_hmac('sha256', $string, $this->secretKey, true));
74+
}
75+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2019 Fabien Potencier
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is furnished
8+
to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Amazon Mailer
2+
=============
3+
4+
Provides Amazon SES integration for Symfony Mailer.
5+
6+
Resources
7+
---------
8+
9+
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
10+
* [Report issues](https://github.com/symfony/symfony/issues) and
11+
[send Pull Requests](https://github.com/symfony/symfony/pulls)
12+
in the [main Symfony repository](https://github.com/symfony/symfony)

0 commit comments

Comments
 (0)