[Mailer] Change the DSN semantics

This commit is contained in:
Fabien Potencier 2019-09-02 13:54:07 +02:00
parent ef2b65aaf1
commit 469c989697
41 changed files with 704 additions and 187 deletions

View File

@ -4,7 +4,7 @@ imports:
framework:
mailer:
dsn: 'smtp://null'
dsn: 'null://null'
envelope:
sender: sender@example.org
recipients:

View File

@ -0,0 +1,48 @@
<?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\Mailer\Bridge\Amazon\Tests\Transport;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mailer\Bridge\Amazon\Transport\SesApiTransport;
class SesApiTransportTest extends TestCase
{
/**
* @dataProvider getTransportData
*/
public function testToString(SesApiTransport $transport, string $expected)
{
$this->assertSame($expected, (string) $transport);
}
public function getTransportData()
{
return [
[
new SesApiTransport('ACCESS_KEY', 'SECRET_KEY'),
'ses+api://ACCESS_KEY@email.eu-west-1.amazonaws.com',
],
[
new SesApiTransport('ACCESS_KEY', 'SECRET_KEY', 'us-east-1'),
'ses+api://ACCESS_KEY@email.us-east-1.amazonaws.com',
],
[
(new SesApiTransport('ACCESS_KEY', 'SECRET_KEY'))->setHost('example.com'),
'ses+api://ACCESS_KEY@example.com',
],
[
(new SesApiTransport('ACCESS_KEY', 'SECRET_KEY'))->setHost('example.com')->setPort(99),
'ses+api://ACCESS_KEY@example.com:99',
],
];
}
}

View File

@ -0,0 +1,48 @@
<?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\Mailer\Bridge\Amazon\Tests\Transport;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mailer\Bridge\Amazon\Transport\SesHttpTransport;
class SesHttpTransportTest extends TestCase
{
/**
* @dataProvider getTransportData
*/
public function testToString(SesHttpTransport $transport, string $expected)
{
$this->assertSame($expected, (string) $transport);
}
public function getTransportData()
{
return [
[
new SesHttpTransport('ACCESS_KEY', 'SECRET_KEY'),
'ses+https://ACCESS_KEY@email.eu-west-1.amazonaws.com',
],
[
new SesHttpTransport('ACCESS_KEY', 'SECRET_KEY', 'us-east-1'),
'ses+https://ACCESS_KEY@email.us-east-1.amazonaws.com',
],
[
(new SesHttpTransport('ACCESS_KEY', 'SECRET_KEY'))->setHost('example.com'),
'ses+https://ACCESS_KEY@example.com',
],
[
(new SesHttpTransport('ACCESS_KEY', 'SECRET_KEY'))->setHost('example.com')->setPort(99),
'ses+https://ACCESS_KEY@example.com:99',
],
];
}
}

View File

@ -29,28 +29,33 @@ class SesTransportFactoryTest extends TransportFactoryTestCase
public function supportsProvider(): iterable
{
yield [
new Dsn('api', 'ses'),
new Dsn('ses+api', 'default'),
true,
];
yield [
new Dsn('http', 'ses'),
new Dsn('ses+https', 'default'),
true,
];
yield [
new Dsn('smtp', 'ses'),
new Dsn('ses', 'default'),
true,
];
yield [
new Dsn('smtps', 'ses'),
new Dsn('ses+smtp', 'default'),
true,
];
yield [
new Dsn('smtp', 'example.com'),
false,
new Dsn('ses+smtps', 'default'),
true,
];
yield [
new Dsn('ses+smtp', 'example.com'),
true,
];
}
@ -61,37 +66,52 @@ class SesTransportFactoryTest extends TransportFactoryTestCase
$logger = $this->getLogger();
yield [
new Dsn('api', 'ses', self::USER, self::PASSWORD),
new Dsn('ses+api', 'default', self::USER, self::PASSWORD),
new SesApiTransport(self::USER, self::PASSWORD, null, $client, $dispatcher, $logger),
];
yield [
new Dsn('api', 'ses', self::USER, self::PASSWORD, null, ['region' => 'eu-west-1']),
new Dsn('ses+api', 'default', self::USER, self::PASSWORD, null, ['region' => 'eu-west-1']),
new SesApiTransport(self::USER, self::PASSWORD, 'eu-west-1', $client, $dispatcher, $logger),
];
yield [
new Dsn('http', 'ses', self::USER, self::PASSWORD),
new Dsn('ses+api', 'example.com', self::USER, self::PASSWORD, 8080),
(new SesApiTransport(self::USER, self::PASSWORD, null, $client, $dispatcher, $logger))->setHost('example.com')->setPort(8080),
];
yield [
new Dsn('ses+https', 'default', self::USER, self::PASSWORD),
new SesHttpTransport(self::USER, self::PASSWORD, null, $client, $dispatcher, $logger),
];
yield [
new Dsn('http', 'ses', self::USER, self::PASSWORD, null, ['region' => 'eu-west-1']),
new Dsn('ses', 'default', self::USER, self::PASSWORD),
new SesHttpTransport(self::USER, self::PASSWORD, null, $client, $dispatcher, $logger),
];
yield [
new Dsn('ses+https', 'example.com', self::USER, self::PASSWORD, 8080),
(new SesHttpTransport(self::USER, self::PASSWORD, null, $client, $dispatcher, $logger))->setHost('example.com')->setPort(8080),
];
yield [
new Dsn('ses+https', 'default', self::USER, self::PASSWORD, null, ['region' => 'eu-west-1']),
new SesHttpTransport(self::USER, self::PASSWORD, 'eu-west-1', $client, $dispatcher, $logger),
];
yield [
new Dsn('smtp', 'ses', self::USER, self::PASSWORD),
new Dsn('ses+smtp', 'default', self::USER, self::PASSWORD),
new SesSmtpTransport(self::USER, self::PASSWORD, null, $dispatcher, $logger),
];
yield [
new Dsn('smtp', 'ses', self::USER, self::PASSWORD, null, ['region' => 'eu-west-1']),
new Dsn('ses+smtp', 'default', self::USER, self::PASSWORD, null, ['region' => 'eu-west-1']),
new SesSmtpTransport(self::USER, self::PASSWORD, 'eu-west-1', $dispatcher, $logger),
];
yield [
new Dsn('smtps', 'ses', self::USER, self::PASSWORD, null, ['region' => 'eu-west-1']),
new Dsn('ses+smtps', 'default', self::USER, self::PASSWORD, null, ['region' => 'eu-west-1']),
new SesSmtpTransport(self::USER, self::PASSWORD, 'eu-west-1', $dispatcher, $logger),
];
}
@ -99,15 +119,15 @@ class SesTransportFactoryTest extends TransportFactoryTestCase
public function unsupportedSchemeProvider(): iterable
{
yield [
new Dsn('foo', 'ses', self::USER, self::PASSWORD),
'The "foo" scheme is not supported for mailer "ses". Supported schemes are: "api", "http", "smtp", "smtps".',
new Dsn('ses+foo', 'default', self::USER, self::PASSWORD),
'The "ses+foo" scheme is not supported. Supported schemes for mailer "ses" are: "ses", "ses+api", "ses+https", "ses+smtp", "ses+smtps".',
];
}
public function incompleteDsnProvider(): iterable
{
yield [new Dsn('smtp', 'ses', self::USER)];
yield [new Dsn('ses+smtp', 'default', self::USER)];
yield [new Dsn('smtp', 'ses', null, self::PASSWORD)];
yield [new Dsn('ses+smtp', 'default', null, self::PASSWORD)];
}
}

View File

@ -25,7 +25,7 @@ use Symfony\Contracts\HttpClient\ResponseInterface;
*/
class SesApiTransport extends AbstractApiTransport
{
private const ENDPOINT = 'https://email.%region%.amazonaws.com';
private const HOST = 'email.%region%.amazonaws.com';
private $accessKey;
private $secretKey;
@ -45,7 +45,7 @@ class SesApiTransport extends AbstractApiTransport
public function __toString(): string
{
return sprintf('api://%s@ses?region=%s', $this->accessKey, $this->region);
return sprintf('ses+api://%s@%s', $this->accessKey, $this->getEndpoint());
}
protected function doSendApi(Email $email, SmtpEnvelope $envelope): ResponseInterface
@ -53,8 +53,7 @@ class SesApiTransport extends AbstractApiTransport
$date = gmdate('D, d M Y H:i:s e');
$auth = sprintf('AWS3-HTTPS AWSAccessKeyId=%s,Algorithm=HmacSHA256,Signature=%s', $this->accessKey, $this->getSignature($date));
$endpoint = str_replace('%region%', $this->region, self::ENDPOINT);
$response = $this->client->request('POST', $endpoint, [
$response = $this->client->request('POST', 'https://'.$this->getEndpoint(), [
'headers' => [
'X-Amzn-Authorization' => $auth,
'Date' => $date,
@ -72,6 +71,11 @@ class SesApiTransport extends AbstractApiTransport
return $response;
}
private function getEndpoint(): ?string
{
return ($this->host ?: str_replace('%region%', $this->region, self::HOST)).($this->port ? ':'.$this->port : '');
}
private function getSignature(string $string): string
{
return base64_encode(hash_hmac('sha256', $string, $this->secretKey, true));

View File

@ -24,7 +24,7 @@ use Symfony\Contracts\HttpClient\ResponseInterface;
*/
class SesHttpTransport extends AbstractHttpTransport
{
private const ENDPOINT = 'https://email.%region%.amazonaws.com';
private const HOST = 'email.%region%.amazonaws.com';
private $accessKey;
private $secretKey;
@ -44,7 +44,7 @@ class SesHttpTransport extends AbstractHttpTransport
public function __toString(): string
{
return sprintf('http://%s@ses?region=%s', $this->accessKey, $this->region);
return sprintf('ses+https://%s@%s', $this->accessKey, $this->getEndpoint());
}
protected function doSendHttp(SentMessage $message): ResponseInterface
@ -52,8 +52,7 @@ class SesHttpTransport extends AbstractHttpTransport
$date = gmdate('D, d M Y H:i:s e');
$auth = sprintf('AWS3-HTTPS AWSAccessKeyId=%s,Algorithm=HmacSHA256,Signature=%s', $this->accessKey, $this->getSignature($date));
$endpoint = str_replace('%region%', $this->region, self::ENDPOINT);
$response = $this->client->request('POST', $endpoint, [
$response = $this->client->request('POST', 'https://'.$this->getEndpoint(), [
'headers' => [
'X-Amzn-Authorization' => $auth,
'Date' => $date,
@ -73,6 +72,11 @@ class SesHttpTransport extends AbstractHttpTransport
return $response;
}
private function getEndpoint(): ?string
{
return ($this->host ?: str_replace('%region%', $this->region, self::HOST)).($this->port ? ':'.$this->port : '');
}
private function getSignature(string $string): string
{
return base64_encode(hash_hmac('sha256', $string, $this->secretKey, true));

View File

@ -27,24 +27,26 @@ final class SesTransportFactory extends AbstractTransportFactory
$user = $this->getUser($dsn);
$password = $this->getPassword($dsn);
$region = $dsn->getOption('region');
$host = 'default' === $dsn->getHost() ? null : $dsn->getHost();
$port = $dsn->getPort();
if ('api' === $scheme) {
return new SesApiTransport($user, $password, $region, $this->client, $this->dispatcher, $this->logger);
if ('ses+api' === $scheme) {
return (new SesApiTransport($user, $password, $region, $this->client, $this->dispatcher, $this->logger))->setHost($host)->setPort($port);
}
if ('http' === $scheme) {
return new SesHttpTransport($user, $password, $region, $this->client, $this->dispatcher, $this->logger);
if ('ses+https' === $scheme || 'ses' === $scheme) {
return (new SesHttpTransport($user, $password, $region, $this->client, $this->dispatcher, $this->logger))->setHost($host)->setPort($port);
}
if ('smtp' === $scheme || 'smtps' === $scheme) {
if ('ses+smtp' === $scheme || 'ses+smtps' === $scheme) {
return new SesSmtpTransport($user, $password, $region, $this->dispatcher, $this->logger);
}
throw new UnsupportedSchemeException($dsn, ['api', 'http', 'smtp', 'smtps']);
throw new UnsupportedSchemeException($dsn, 'ses', $this->getSupportedSchemes());
}
public function supports(Dsn $dsn): bool
protected function getSupportedSchemes(): array
{
return 'ses' === $dsn->getHost();
return ['ses', 'ses+api', 'ses+https', 'ses+smtp', 'ses+smtps'];
}
}

View File

@ -18,30 +18,40 @@ class GmailTransportFactoryTest extends TransportFactoryTestCase
public function supportsProvider(): iterable
{
yield [
new Dsn('smtp', 'gmail'),
new Dsn('gmail', 'default'),
true,
];
yield [
new Dsn('smtps', 'gmail'),
new Dsn('gmail+smtp', 'default'),
true,
];
yield [
new Dsn('smtp', 'example.com'),
false,
new Dsn('gmail+smtps', 'default'),
true,
];
yield [
new Dsn('gmail+smtp', 'example.com'),
true,
];
}
public function createProvider(): iterable
{
yield [
new Dsn('smtp', 'gmail', self::USER, self::PASSWORD),
new Dsn('gmail', 'default', self::USER, self::PASSWORD),
new GmailSmtpTransport(self::USER, self::PASSWORD, $this->getDispatcher(), $this->getLogger()),
];
yield [
new Dsn('smtps', 'gmail', self::USER, self::PASSWORD),
new Dsn('gmail+smtp', 'default', self::USER, self::PASSWORD),
new GmailSmtpTransport(self::USER, self::PASSWORD, $this->getDispatcher(), $this->getLogger()),
];
yield [
new Dsn('gmail+smtps', 'default', self::USER, self::PASSWORD),
new GmailSmtpTransport(self::USER, self::PASSWORD, $this->getDispatcher(), $this->getLogger()),
];
}
@ -49,15 +59,15 @@ class GmailTransportFactoryTest extends TransportFactoryTestCase
public function unsupportedSchemeProvider(): iterable
{
yield [
new Dsn('foo', 'gmail', self::USER, self::PASSWORD),
'The "foo" scheme is not supported for mailer "gmail". Supported schemes are: "smtp", "smtps".',
new Dsn('gmail+foo', 'default', self::USER, self::PASSWORD),
'The "gmail+foo" scheme is not supported. Supported schemes for mailer "gmail" are: "gmail", "gmail+smtp", "gmail+smtps".',
];
}
public function incompleteDsnProvider(): iterable
{
yield [new Dsn('smtp', 'gmail', self::USER)];
yield [new Dsn('gmail+smtp', 'default', self::USER)];
yield [new Dsn('smtp', 'gmail', null, self::PASSWORD)];
yield [new Dsn('gmail+smtp', 'default', null, self::PASSWORD)];
}
}

View File

@ -23,15 +23,15 @@ final class GmailTransportFactory extends AbstractTransportFactory
{
public function create(Dsn $dsn): TransportInterface
{
if ('smtp' === $dsn->getScheme() || 'smtps' === $dsn->getScheme()) {
if (\in_array($dsn->getScheme(), $this->getSupportedSchemes())) {
return new GmailSmtpTransport($this->getUser($dsn), $this->getPassword($dsn), $this->dispatcher, $this->logger);
}
throw new UnsupportedSchemeException($dsn, ['smtp', 'smtps']);
throw new UnsupportedSchemeException($dsn, 'gmail', $this->getSupportedSchemes());
}
public function supports(Dsn $dsn): bool
protected function getSupportedSchemes(): array
{
return 'gmail' === $dsn->getHost();
return ['gmail', 'gmail+smtp', 'gmail+smtps'];
}
}

View File

@ -0,0 +1,44 @@
<?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\Mailer\Bridge\Mailchimp\Tests\Transport;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mailer\Bridge\Mailchimp\Transport\MandrillApiTransport;
class MandrillApiTransportTest extends TestCase
{
/**
* @dataProvider getTransportData
*/
public function testToString(MandrillApiTransport $transport, string $expected)
{
$this->assertSame($expected, (string) $transport);
}
public function getTransportData()
{
return [
[
new MandrillApiTransport('KEY'),
'mandrill+api://mandrillapp.com',
],
[
(new MandrillApiTransport('KEY'))->setHost('example.com'),
'mandrill+api://example.com',
],
[
(new MandrillApiTransport('KEY'))->setHost('example.com')->setPort(99),
'mandrill+api://example.com:99',
],
];
}
}

View File

@ -0,0 +1,44 @@
<?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\Mailer\Bridge\Mailchimp\Tests\Transport;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mailer\Bridge\Mailchimp\Transport\MandrillHttpTransport;
class MandrillHttpTransportTest extends TestCase
{
/**
* @dataProvider getTransportData
*/
public function testToString(MandrillHttpTransport $transport, string $expected)
{
$this->assertSame($expected, (string) $transport);
}
public function getTransportData()
{
return [
[
new MandrillHttpTransport('KEY'),
'mandrill+https://mandrillapp.com',
],
[
(new MandrillHttpTransport('KEY'))->setHost('example.com'),
'mandrill+https://example.com',
],
[
(new MandrillHttpTransport('KEY'))->setHost('example.com')->setPort(99),
'mandrill+https://example.com:99',
],
];
}
}

View File

@ -29,28 +29,33 @@ class MandrillTransportFactoryTest extends TransportFactoryTestCase
public function supportsProvider(): iterable
{
yield [
new Dsn('api', 'mandrill'),
new Dsn('mandrill', 'default'),
true,
];
yield [
new Dsn('http', 'mandrill'),
new Dsn('mandrill+api', 'default'),
true,
];
yield [
new Dsn('smtp', 'mandrill'),
new Dsn('mandrill+https', 'default'),
true,
];
yield [
new Dsn('smtps', 'mandrill'),
new Dsn('mandrill+smtp', 'default'),
true,
];
yield [
new Dsn('smtp', 'example.com'),
false,
new Dsn('mandrill+smtps', 'default'),
true,
];
yield [
new Dsn('mandrill+smtp', 'example.com'),
true,
];
}
@ -61,22 +66,37 @@ class MandrillTransportFactoryTest extends TransportFactoryTestCase
$logger = $this->getLogger();
yield [
new Dsn('api', 'mandrill', self::USER),
new Dsn('mandrill+api', 'default', self::USER),
new MandrillApiTransport(self::USER, $client, $dispatcher, $logger),
];
yield [
new Dsn('http', 'mandrill', self::USER),
new Dsn('mandrill+api', 'example.com', self::USER, '', 8080),
(new MandrillApiTransport(self::USER, $client, $dispatcher, $logger))->setHost('example.com')->setPort(8080),
];
yield [
new Dsn('mandrill', 'default', self::USER),
new MandrillHttpTransport(self::USER, $client, $dispatcher, $logger),
];
yield [
new Dsn('smtp', 'mandrill', self::USER, self::PASSWORD),
new Dsn('mandrill+https', 'default', self::USER),
new MandrillHttpTransport(self::USER, $client, $dispatcher, $logger),
];
yield [
new Dsn('mandrill+https', 'example.com', self::USER, '', 8080),
(new MandrillHttpTransport(self::USER, $client, $dispatcher, $logger))->setHost('example.com')->setPort(8080),
];
yield [
new Dsn('mandrill+smtp', 'default', self::USER, self::PASSWORD),
new MandrillSmtpTransport(self::USER, self::PASSWORD, $dispatcher, $logger),
];
yield [
new Dsn('smtps', 'mandrill', self::USER, self::PASSWORD),
new Dsn('mandrill+smtps', 'default', self::USER, self::PASSWORD),
new MandrillSmtpTransport(self::USER, self::PASSWORD, $dispatcher, $logger),
];
}
@ -84,15 +104,15 @@ class MandrillTransportFactoryTest extends TransportFactoryTestCase
public function unsupportedSchemeProvider(): iterable
{
yield [
new Dsn('foo', 'mandrill', self::USER),
'The "foo" scheme is not supported for mailer "mandrill". Supported schemes are: "api", "http", "smtp", "smtps".',
new Dsn('mandrill+foo', 'default', self::USER),
'The "mandrill+foo" scheme is not supported. Supported schemes for mailer "mandrill" are: "mandrill", "mandrill+api", "mandrill+https", "mandrill+smtp", "mandrill+smtps".',
];
}
public function incompleteDsnProvider(): iterable
{
yield [new Dsn('api', 'mandrill')];
yield [new Dsn('mandrill+api', 'default')];
yield [new Dsn('smtp', 'mandrill', self::USER)];
yield [new Dsn('mandrill+smtp', 'default', self::USER)];
}
}

View File

@ -25,7 +25,7 @@ use Symfony\Contracts\HttpClient\ResponseInterface;
*/
class MandrillApiTransport extends AbstractApiTransport
{
private const ENDPOINT = 'https://mandrillapp.com/api/1.0/messages/send.json';
private const HOST = 'mandrillapp.com';
private $key;
@ -38,12 +38,12 @@ class MandrillApiTransport extends AbstractApiTransport
public function __toString(): string
{
return sprintf('api://mandrill');
return sprintf('mandrill+api://%s', $this->getEndpoint());
}
protected function doSendApi(Email $email, SmtpEnvelope $envelope): ResponseInterface
{
$response = $this->client->request('POST', self::ENDPOINT, [
$response = $this->client->request('POST', 'https://'.$this->getEndpoint().'/api/1.0/messages/send.json', [
'json' => $this->getPayload($email, $envelope),
]);
@ -59,6 +59,11 @@ class MandrillApiTransport extends AbstractApiTransport
return $response;
}
private function getEndpoint(): ?string
{
return ($this->host ?: self::HOST).($this->port ? ':'.$this->port : '');
}
private function getPayload(Email $email, SmtpEnvelope $envelope): array
{
$payload = [

View File

@ -24,7 +24,7 @@ use Symfony\Contracts\HttpClient\ResponseInterface;
*/
class MandrillHttpTransport extends AbstractHttpTransport
{
private const ENDPOINT = 'https://mandrillapp.com/api/1.0/messages/send-raw.json';
private const HOST = 'mandrillapp.com';
private $key;
public function __construct(string $key, HttpClientInterface $client = null, EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null)
@ -36,13 +36,13 @@ class MandrillHttpTransport extends AbstractHttpTransport
public function __toString(): string
{
return sprintf('http://mandrill');
return sprintf('mandrill+https://%s', $this->getEndpoint());
}
protected function doSendHttp(SentMessage $message): ResponseInterface
{
$envelope = $message->getEnvelope();
$response = $this->client->request('POST', self::ENDPOINT, [
$response = $this->client->request('POST', 'https://'.$this->getEndpoint().'/api/1.0/messages/send-raw.json', [
'json' => [
'key' => $this->key,
'to' => $this->stringifyAddresses($envelope->getRecipients()),
@ -62,4 +62,9 @@ class MandrillHttpTransport extends AbstractHttpTransport
return $response;
}
private function getEndpoint(): ?string
{
return ($this->host ?: self::HOST).($this->port ? ':'.$this->port : '');
}
}

View File

@ -25,26 +25,28 @@ final class MandrillTransportFactory extends AbstractTransportFactory
{
$scheme = $dsn->getScheme();
$user = $this->getUser($dsn);
$host = 'default' === $dsn->getHost() ? null : $dsn->getHost();
$port = $dsn->getPort();
if ('api' === $scheme) {
return new MandrillApiTransport($user, $this->client, $this->dispatcher, $this->logger);
if ('mandrill+api' === $scheme) {
return (new MandrillApiTransport($user, $this->client, $this->dispatcher, $this->logger))->setHost($host)->setPort($port);
}
if ('http' === $scheme) {
return new MandrillHttpTransport($user, $this->client, $this->dispatcher, $this->logger);
if ('mandrill+https' === $scheme || 'mandrill' === $scheme) {
return (new MandrillHttpTransport($user, $this->client, $this->dispatcher, $this->logger))->setHost($host)->setPort($port);
}
if ('smtp' === $scheme || 'smtps' === $scheme) {
if ('mandrill+smtp' === $scheme || 'mandrill+smtps' === $scheme) {
$password = $this->getPassword($dsn);
return new MandrillSmtpTransport($user, $password, $this->dispatcher, $this->logger);
}
throw new UnsupportedSchemeException($dsn, ['api', 'http', 'smtp', 'smtps']);
throw new UnsupportedSchemeException($dsn, 'mandrill', $this->getSupportedSchemes());
}
public function supports(Dsn $dsn): bool
protected function getSupportedSchemes(): array
{
return 'mandrill' === $dsn->getHost();
return ['mandrill', 'mandrill+api', 'mandrill+https', 'mandrill+smtp', 'mandrill+smtps'];
}
}

View File

@ -0,0 +1,48 @@
<?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\Mailer\Bridge\Mailgun\Tests\Transport;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mailer\Bridge\Mailgun\Transport\MailgunApiTransport;
class MailgunApiTransportTest extends TestCase
{
/**
* @dataProvider getTransportData
*/
public function testToString(MailgunApiTransport $transport, string $expected)
{
$this->assertSame($expected, (string) $transport);
}
public function getTransportData()
{
return [
[
new MailgunApiTransport('ACCESS_KEY', 'DOMAIN'),
'mailgun+api://api.mailgun.net?domain=DOMAIN',
],
[
new MailgunApiTransport('ACCESS_KEY', 'DOMAIN', 'us-east-1'),
'mailgun+api://api.us-east-1.mailgun.net?domain=DOMAIN',
],
[
(new MailgunApiTransport('ACCESS_KEY', 'DOMAIN'))->setHost('example.com'),
'mailgun+api://example.com?domain=DOMAIN',
],
[
(new MailgunApiTransport('ACCESS_KEY', 'DOMAIN'))->setHost('example.com')->setPort(99),
'mailgun+api://example.com:99?domain=DOMAIN',
],
];
}
}

View File

@ -0,0 +1,48 @@
<?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\Mailer\Bridge\Mailgun\Tests\Transport;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mailer\Bridge\Mailgun\Transport\MailgunHttpTransport;
class MailgunHttpTransportTest extends TestCase
{
/**
* @dataProvider getTransportData
*/
public function testToString(MailgunHttpTransport $transport, string $expected)
{
$this->assertSame($expected, (string) $transport);
}
public function getTransportData()
{
return [
[
new MailgunHttpTransport('ACCESS_KEY', 'DOMAIN'),
'mailgun+https://api.mailgun.net?domain=DOMAIN',
],
[
new MailgunHttpTransport('ACCESS_KEY', 'DOMAIN', 'us-east-1'),
'mailgun+https://api.us-east-1.mailgun.net?domain=DOMAIN',
],
[
(new MailgunHttpTransport('ACCESS_KEY', 'DOMAIN'))->setHost('example.com'),
'mailgun+https://example.com?domain=DOMAIN',
],
[
(new MailgunHttpTransport('ACCESS_KEY', 'DOMAIN'))->setHost('example.com')->setPort(99),
'mailgun+https://example.com:99?domain=DOMAIN',
],
];
}
}

View File

@ -29,28 +29,33 @@ class MailgunTransportFactoryTest extends TransportFactoryTestCase
public function supportsProvider(): iterable
{
yield [
new Dsn('api', 'mailgun'),
new Dsn('mailgun+api', 'default'),
true,
];
yield [
new Dsn('http', 'mailgun'),
new Dsn('mailgun', 'default'),
true,
];
yield [
new Dsn('smtp', 'mailgun'),
new Dsn('mailgun+https', 'default'),
true,
];
yield [
new Dsn('smtps', 'mailgun'),
new Dsn('mailgun+smtp', 'default'),
true,
];
yield [
new Dsn('smtp', 'example.com'),
false,
new Dsn('mailgun+smtps', 'default'),
true,
];
yield [
new Dsn('mailgun+smtp', 'example.com'),
true,
];
}
@ -61,27 +66,42 @@ class MailgunTransportFactoryTest extends TransportFactoryTestCase
$logger = $this->getLogger();
yield [
new Dsn('api', 'mailgun', self::USER, self::PASSWORD),
new Dsn('mailgun+api', 'default', self::USER, self::PASSWORD),
new MailgunApiTransport(self::USER, self::PASSWORD, null, $client, $dispatcher, $logger),
];
yield [
new Dsn('api', 'mailgun', self::USER, self::PASSWORD, null, ['region' => 'eu']),
new Dsn('mailgun+api', 'default', self::USER, self::PASSWORD, null, ['region' => 'eu']),
new MailgunApiTransport(self::USER, self::PASSWORD, 'eu', $client, $dispatcher, $logger),
];
yield [
new Dsn('http', 'mailgun', self::USER, self::PASSWORD),
new Dsn('mailgun+api', 'example.com', self::USER, self::PASSWORD, 8080),
(new MailgunApiTransport(self::USER, self::PASSWORD, null, $client, $dispatcher, $logger))->setHost('example.com')->setPort(8080),
];
yield [
new Dsn('mailgun', 'default', self::USER, self::PASSWORD),
new MailgunHttpTransport(self::USER, self::PASSWORD, null, $client, $dispatcher, $logger),
];
yield [
new Dsn('smtp', 'mailgun', self::USER, self::PASSWORD),
new Dsn('mailgun+https', 'default', self::USER, self::PASSWORD),
new MailgunHttpTransport(self::USER, self::PASSWORD, null, $client, $dispatcher, $logger),
];
yield [
new Dsn('mailgun+https', 'example.com', self::USER, self::PASSWORD, 8080),
(new MailgunHttpTransport(self::USER, self::PASSWORD, null, $client, $dispatcher, $logger))->setHost('example.com')->setPort(8080),
];
yield [
new Dsn('mailgun+smtp', 'default', self::USER, self::PASSWORD),
new MailgunSmtpTransport(self::USER, self::PASSWORD, null, $dispatcher, $logger),
];
yield [
new Dsn('smtps', 'mailgun', self::USER, self::PASSWORD),
new Dsn('mailgun+smtps', 'default', self::USER, self::PASSWORD),
new MailgunSmtpTransport(self::USER, self::PASSWORD, null, $dispatcher, $logger),
];
}
@ -89,15 +109,15 @@ class MailgunTransportFactoryTest extends TransportFactoryTestCase
public function unsupportedSchemeProvider(): iterable
{
yield [
new Dsn('foo', 'mailgun', self::USER, self::PASSWORD),
'The "foo" scheme is not supported for mailer "mailgun". Supported schemes are: "api", "http", "smtp", "smtps".',
new Dsn('mailgun+foo', 'default', self::USER, self::PASSWORD),
'The "mailgun+foo" scheme is not supported. Supported schemes for mailer "mailgun" are: "mailgun", "mailgun+api", "mailgun+https", "mailgun+smtp", "mailgun+smtps".',
];
}
public function incompleteDsnProvider(): iterable
{
yield [new Dsn('api', 'mailgun', self::USER)];
yield [new Dsn('mailgun+api', 'default', self::USER)];
yield [new Dsn('api', 'mailgun', null, self::PASSWORD)];
yield [new Dsn('mailgun+api', 'default', null, self::PASSWORD)];
}
}

View File

@ -26,7 +26,7 @@ use Symfony\Contracts\HttpClient\ResponseInterface;
*/
class MailgunApiTransport extends AbstractApiTransport
{
private const ENDPOINT = 'https://api.%region_dot%mailgun.net/v3/%domain%/messages';
private const HOST = 'api.%region_dot%mailgun.net';
private $key;
private $domain;
@ -43,7 +43,7 @@ class MailgunApiTransport extends AbstractApiTransport
public function __toString(): string
{
return sprintf('api://%s@mailgun?region=%s', $this->domain, $this->region);
return sprintf('mailgun+api://%s?domain=%s', $this->getEndpoint(), $this->domain);
}
protected function doSendApi(Email $email, SmtpEnvelope $envelope): ResponseInterface
@ -54,8 +54,8 @@ class MailgunApiTransport extends AbstractApiTransport
$headers[] = $header->toString();
}
$endpoint = str_replace(['%domain%', '%region_dot%'], [urlencode($this->domain), 'us' !== ($this->region ?: 'us') ? $this->region.'.' : ''], self::ENDPOINT);
$response = $this->client->request('POST', $endpoint, [
$endpoint = str_replace('%domain%', urlencode($this->domain), $this->getEndpoint()).'/v3/%domain%/messages';
$response = $this->client->request('POST', 'https://'.$endpoint, [
'auth_basic' => 'api:'.$this->key,
'headers' => $headers,
'body' => $body->bodyToIterable(),
@ -137,4 +137,11 @@ class MailgunApiTransport extends AbstractApiTransport
return [$attachments, $inlines, $html];
}
private function getEndpoint(): ?string
{
$host = $this->host ?: str_replace('%region_dot%', 'us' !== ($this->region ?: 'us') ? $this->region.'.' : '', self::HOST);
return $host.($this->port ? ':'.$this->port : '');
}
}

View File

@ -26,7 +26,8 @@ use Symfony\Contracts\HttpClient\ResponseInterface;
*/
class MailgunHttpTransport extends AbstractHttpTransport
{
private const ENDPOINT = 'https://api.%region_dot%mailgun.net/v3/%domain%/messages.mime';
private const HOST = 'api.%region_dot%mailgun.net';
private $key;
private $domain;
private $region;
@ -42,7 +43,7 @@ class MailgunHttpTransport extends AbstractHttpTransport
public function __toString(): string
{
return sprintf('http://%s@mailgun?region=%s', $this->domain, $this->region);
return sprintf('mailgun+https://%s?domain=%s', $this->getEndpoint(), $this->domain);
}
protected function doSendHttp(SentMessage $message): ResponseInterface
@ -55,8 +56,9 @@ class MailgunHttpTransport extends AbstractHttpTransport
foreach ($body->getPreparedHeaders()->all() as $header) {
$headers[] = $header->toString();
}
$endpoint = str_replace(['%domain%', '%region_dot%'], [urlencode($this->domain), 'us' !== ($this->region ?: 'us') ? $this->region.'.' : ''], self::ENDPOINT);
$response = $this->client->request('POST', $endpoint, [
$endpoint = str_replace('%domain%', urlencode($this->domain), $this->getEndpoint()).'/v3/%domain%/messages.mime';
$response = $this->client->request('POST', 'https://'.$endpoint, [
'auth_basic' => 'api:'.$this->key,
'headers' => $headers,
'body' => $body->bodyToIterable(),
@ -70,4 +72,11 @@ class MailgunHttpTransport extends AbstractHttpTransport
return $response;
}
private function getEndpoint(): ?string
{
$host = $this->host ?: str_replace('%region_dot%', 'us' !== ($this->region ?: 'us') ? $this->region.'.' : '', self::HOST);
return $host.($this->port ? ':'.$this->port : '');
}
}

View File

@ -27,24 +27,26 @@ final class MailgunTransportFactory extends AbstractTransportFactory
$user = $this->getUser($dsn);
$password = $this->getPassword($dsn);
$region = $dsn->getOption('region');
$host = 'default' === $dsn->getHost() ? null : $dsn->getHost();
$port = $dsn->getPort();
if ('api' === $scheme) {
return new MailgunApiTransport($user, $password, $region, $this->client, $this->dispatcher, $this->logger);
if ('mailgun+api' === $scheme) {
return (new MailgunApiTransport($user, $password, $region, $this->client, $this->dispatcher, $this->logger))->setHost($host)->setPort($port);
}
if ('http' === $scheme) {
return new MailgunHttpTransport($user, $password, $region, $this->client, $this->dispatcher, $this->logger);
if ('mailgun+https' === $scheme || 'mailgun' === $scheme) {
return (new MailgunHttpTransport($user, $password, $region, $this->client, $this->dispatcher, $this->logger))->setHost($host)->setPort($port);
}
if ('smtp' === $scheme || 'smtps' === $scheme) {
if ('mailgun+smtp' === $scheme || 'mailgun+smtps' === $scheme) {
return new MailgunSmtpTransport($user, $password, $region, $this->dispatcher, $this->logger);
}
throw new UnsupportedSchemeException($dsn, ['api', 'http', 'smtp', 'smtps']);
throw new UnsupportedSchemeException($dsn, 'mailgun', $this->getSupportedSchemes());
}
public function supports(Dsn $dsn): bool
protected function getSupportedSchemes(): array
{
return 'mailgun' === $dsn->getHost();
return ['mailgun', 'mailgun+api', 'mailgun+https', 'mailgun+smtp', 'mailgun+smtps'];
}
}

View File

@ -0,0 +1,44 @@
<?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\Mailer\Bridge\Postmark\Tests\Transport;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkApiTransport;
class PostmarkApiTransportTest extends TestCase
{
/**
* @dataProvider getTransportData
*/
public function testToString(PostmarkApiTransport $transport, string $expected)
{
$this->assertSame($expected, (string) $transport);
}
public function getTransportData()
{
return [
[
new PostmarkApiTransport('KEY'),
'postmark+api://api.postmarkapp.com',
],
[
(new PostmarkApiTransport('KEY'))->setHost('example.com'),
'postmark+api://example.com',
],
[
(new PostmarkApiTransport('KEY'))->setHost('example.com')->setPort(99),
'postmark+api://example.com:99',
],
];
}
}

View File

@ -28,23 +28,28 @@ class PostmarkTransportFactoryTest extends TransportFactoryTestCase
public function supportsProvider(): iterable
{
yield [
new Dsn('api', 'postmark'),
new Dsn('postmark+api', 'default'),
true,
];
yield [
new Dsn('smtp', 'postmark'),
new Dsn('postmark', 'default'),
true,
];
yield [
new Dsn('smtps', 'postmark'),
new Dsn('postmark+smtp', 'default'),
true,
];
yield [
new Dsn('smtp', 'example.com'),
false,
new Dsn('postmark+smtps', 'default'),
true,
];
yield [
new Dsn('postmark+smtp', 'example.com'),
true,
];
}
@ -54,17 +59,27 @@ class PostmarkTransportFactoryTest extends TransportFactoryTestCase
$logger = $this->getLogger();
yield [
new Dsn('api', 'postmark', self::USER),
new Dsn('postmark+api', 'default', self::USER),
new PostmarkApiTransport(self::USER, $this->getClient(), $dispatcher, $logger),
];
yield [
new Dsn('smtp', 'postmark', self::USER),
new Dsn('postmark+api', 'example.com', self::USER, '', 8080),
(new PostmarkApiTransport(self::USER, $this->getClient(), $dispatcher, $logger))->setHost('example.com')->setPort(8080),
];
yield [
new Dsn('postmark', 'default', self::USER),
new PostmarkSmtpTransport(self::USER, $dispatcher, $logger),
];
yield [
new Dsn('smtps', 'postmark', self::USER),
new Dsn('postmark+smtp', 'default', self::USER),
new PostmarkSmtpTransport(self::USER, $dispatcher, $logger),
];
yield [
new Dsn('postmark+smtps', 'default', self::USER),
new PostmarkSmtpTransport(self::USER, $dispatcher, $logger),
];
}
@ -72,13 +87,13 @@ class PostmarkTransportFactoryTest extends TransportFactoryTestCase
public function unsupportedSchemeProvider(): iterable
{
yield [
new Dsn('foo', 'postmark', self::USER),
'The "foo" scheme is not supported for mailer "postmark". Supported schemes are: "api", "smtp", "smtps".',
new Dsn('postmark+foo', 'default', self::USER),
'The "postmark+foo" scheme is not supported. Supported schemes for mailer "postmark" are: "postmark", "postmark+api", "postmark+smtp", "postmark+smtps".',
];
}
public function incompleteDsnProvider(): iterable
{
yield [new Dsn('api', 'postmark')];
yield [new Dsn('postmark+api', 'default')];
}
}

View File

@ -25,7 +25,7 @@ use Symfony\Contracts\HttpClient\ResponseInterface;
*/
class PostmarkApiTransport extends AbstractApiTransport
{
private const ENDPOINT = 'http://api.postmarkapp.com/email';
private const HOST = 'api.postmarkapp.com';
private $key;
@ -38,12 +38,12 @@ class PostmarkApiTransport extends AbstractApiTransport
public function __toString(): string
{
return sprintf('api://postmark');
return sprintf('postmark+api://%s', $this->getEndpoint());
}
protected function doSendApi(Email $email, SmtpEnvelope $envelope): ResponseInterface
{
$response = $this->client->request('POST', self::ENDPOINT, [
$response = $this->client->request('POST', 'https://'.$this->getEndpoint().'/email', [
'headers' => [
'Accept' => 'application/json',
'X-Postmark-Server-Token' => $this->key,
@ -111,4 +111,9 @@ class PostmarkApiTransport extends AbstractApiTransport
return $attachments;
}
private function getEndpoint(): ?string
{
return ($this->host ?: self::HOST).($this->port ? ':'.$this->port : '');
}
}

View File

@ -26,19 +26,22 @@ final class PostmarkTransportFactory extends AbstractTransportFactory
$scheme = $dsn->getScheme();
$user = $this->getUser($dsn);
if ('api' === $scheme) {
return new PostmarkApiTransport($user, $this->client, $this->dispatcher, $this->logger);
if ('postmark+api' === $scheme) {
$host = 'default' === $dsn->getHost() ? null : $dsn->getHost();
$port = $dsn->getPort();
return (new PostmarkApiTransport($user, $this->client, $this->dispatcher, $this->logger))->setHost($host)->setPort($port);
}
if ('smtp' === $scheme || 'smtps' === $scheme) {
if ('postmark+smtp' === $scheme || 'postmark+smtps' === $scheme || 'postmark' === $scheme) {
return new PostmarkSmtpTransport($user, $this->dispatcher, $this->logger);
}
throw new UnsupportedSchemeException($dsn, ['api', 'smtp', 'smtps']);
throw new UnsupportedSchemeException($dsn, 'postmark', $this->getSupportedSchemes());
}
public function supports(Dsn $dsn): bool
protected function getSupportedSchemes(): array
{
return 'postmark' === $dsn->getHost();
return ['postmark', 'postmark+api', 'postmark+smtp', 'postmark+smtps'];
}
}

View File

@ -19,6 +19,32 @@ use Symfony\Contracts\HttpClient\ResponseInterface;
class SendgridApiTransportTest extends TestCase
{
/**
* @dataProvider getTransportData
*/
public function testToString(SendgridApiTransport $transport, string $expected)
{
$this->assertSame($expected, (string) $transport);
}
public function getTransportData()
{
return [
[
new SendgridApiTransport('KEY'),
'sendgrid+api://api.sendgrid.com',
],
[
(new SendgridApiTransport('KEY'))->setHost('example.com'),
'sendgrid+api://example.com',
],
[
(new SendgridApiTransport('KEY'))->setHost('example.com')->setPort(99),
'sendgrid+api://example.com:99',
],
];
}
public function testSend()
{
$email = new Email();

View File

@ -28,23 +28,28 @@ class SendgridTransportFactoryTest extends TransportFactoryTestCase
public function supportsProvider(): iterable
{
yield [
new Dsn('api', 'sendgrid'),
new Dsn('sendgrid+api', 'default'),
true,
];
yield [
new Dsn('smtp', 'sendgrid'),
new Dsn('sendgrid', 'default'),
true,
];
yield [
new Dsn('smtps', 'sendgrid'),
new Dsn('sendgrid+smtp', 'default'),
true,
];
yield [
new Dsn('smtp', 'example.com'),
false,
new Dsn('sendgrid+smtps', 'default'),
true,
];
yield [
new Dsn('sendgrid+smtp', 'example.com'),
true,
];
}
@ -54,17 +59,27 @@ class SendgridTransportFactoryTest extends TransportFactoryTestCase
$logger = $this->getLogger();
yield [
new Dsn('api', 'sendgrid', self::USER),
new Dsn('sendgrid+api', 'default', self::USER),
new SendgridApiTransport(self::USER, $this->getClient(), $dispatcher, $logger),
];
yield [
new Dsn('smtp', 'sendgrid', self::USER),
new Dsn('sendgrid+api', 'example.com', self::USER, '', 8080),
(new SendgridApiTransport(self::USER, $this->getClient(), $dispatcher, $logger))->setHost('example.com')->setPort(8080),
];
yield [
new Dsn('sendgrid', 'default', self::USER),
new SendgridSmtpTransport(self::USER, $dispatcher, $logger),
];
yield [
new Dsn('smtps', 'sendgrid', self::USER),
new Dsn('sendgrid+smtp', 'default', self::USER),
new SendgridSmtpTransport(self::USER, $dispatcher, $logger),
];
yield [
new Dsn('sendgrid+smtps', 'default', self::USER),
new SendgridSmtpTransport(self::USER, $dispatcher, $logger),
];
}
@ -72,13 +87,13 @@ class SendgridTransportFactoryTest extends TransportFactoryTestCase
public function unsupportedSchemeProvider(): iterable
{
yield [
new Dsn('foo', 'sendgrid', self::USER),
'The "foo" scheme is not supported for mailer "sendgrid". Supported schemes are: "api", "smtp", "smtps".',
new Dsn('sendgrid+foo', 'sendgrid', self::USER),
'The "sendgrid+foo" scheme is not supported. Supported schemes for mailer "sendgrid" are: "sendgrid", "sendgrid+api", "sendgrid+smtp", "sendgrid+smtps".',
];
}
public function incompleteDsnProvider(): iterable
{
yield [new Dsn('api', 'sendgrid')];
yield [new Dsn('sendgrid+api', 'default')];
}
}

View File

@ -26,7 +26,7 @@ use Symfony\Contracts\HttpClient\ResponseInterface;
*/
class SendgridApiTransport extends AbstractApiTransport
{
private const ENDPOINT = 'https://api.sendgrid.com/v3/mail/send';
private const HOST = 'api.sendgrid.com';
private $key;
@ -39,12 +39,12 @@ class SendgridApiTransport extends AbstractApiTransport
public function __toString(): string
{
return sprintf('api://sendgrid');
return sprintf('sendgrid+api://%s', $this->getEndpoint());
}
protected function doSendApi(Email $email, SmtpEnvelope $envelope): ResponseInterface
{
$response = $this->client->request('POST', self::ENDPOINT, [
$response = $this->client->request('POST', 'https://'.$this->getEndpoint().'/v3/mail/send', [
'json' => $this->getPayload($email, $envelope),
'auth_bearer' => $this->key,
]);
@ -136,4 +136,9 @@ class SendgridApiTransport extends AbstractApiTransport
return $attachments;
}
private function getEndpoint(): ?string
{
return ($this->host ?: self::HOST).($this->port ? ':'.$this->port : '');
}
}

View File

@ -25,19 +25,22 @@ final class SendgridTransportFactory extends AbstractTransportFactory
{
$key = $this->getUser($dsn);
if ('api' === $dsn->getScheme()) {
return new SendgridApiTransport($key, $this->client, $this->dispatcher, $this->logger);
if ('sendgrid+api' === $dsn->getScheme()) {
$host = 'default' === $dsn->getHost() ? null : $dsn->getHost();
$port = $dsn->getPort();
return (new SendgridApiTransport($key, $this->client, $this->dispatcher, $this->logger))->setHost($host)->setPort($port);
}
if ('smtp' === $dsn->getScheme() || 'smtps' === $dsn->getScheme()) {
if ('sendgrid+smtp' === $dsn->getScheme() || 'sendgrid+smtps' === $dsn->getScheme() || 'sendgrid' === $dsn->getScheme()) {
return new SendgridSmtpTransport($key, $this->dispatcher, $this->logger);
}
throw new UnsupportedSchemeException($dsn, ['api', 'smtp', 'smtps']);
throw new UnsupportedSchemeException($dsn, 'sendgrid', $this->getSupportedSchemes());
}
public function supports(Dsn $dsn): bool
protected function getSupportedSchemes(): array
{
return 'sendgrid' === $dsn->getHost();
return ['sendgrid', 'sendgrid+api', 'sendgrid+smtp', 'sendgrid+smtps'];
}
}

View File

@ -18,8 +18,8 @@ use Symfony\Component\Mailer\Transport\Dsn;
*/
class UnsupportedSchemeException extends LogicException
{
public function __construct(Dsn $dsn, array $supported)
public function __construct(Dsn $dsn, string $name, array $supported)
{
parent::__construct(sprintf('The "%s" scheme is not supported for mailer "%s". Supported schemes are: "%s".', $dsn->getScheme(), $dsn->getHost(), implode('", "', $supported)));
parent::__construct(sprintf('The "%s" scheme is not supported. Supported schemes for mailer "%s" are: "%s".', $dsn->getScheme(), $name, implode('", "', $supported)));
}
}

View File

@ -69,7 +69,7 @@ abstract class TransportFactoryTestCase extends TestCase
$factory = $this->getFactory();
$this->assertEquals($transport, $factory->create($dsn));
if ('smtp' !== $dsn->getScheme() && 'smtps' !== $dsn->getScheme()) {
if (false !== strpos('smtp', $dsn->getScheme())) {
$this->assertStringMatchesFormat($dsn->getScheme().'://%S'.$dsn->getHost().'%S', (string) $transport);
}
}

View File

@ -27,29 +27,16 @@ class NullTransportFactoryTest extends TransportFactoryTestCase
public function supportsProvider(): iterable
{
yield [
new Dsn('smtp', 'null'),
new Dsn('null', ''),
true,
];
yield [
new Dsn('smtp', 'example.com'),
false,
];
}
public function createProvider(): iterable
{
yield [
new Dsn('smtp', 'null'),
new Dsn('null', 'null'),
new NullTransport($this->getDispatcher(), $this->getLogger()),
];
}
public function unsupportedSchemeProvider(): iterable
{
yield [
new Dsn('foo', 'null'),
'The "foo" scheme is not supported for mailer "null". Supported schemes are: "smtp".',
];
}
}

View File

@ -19,6 +19,6 @@ class NullTransportTest extends TestCase
public function testToString()
{
$t = new NullTransport();
$this->assertEquals('smtp://null', (string) $t);
$this->assertEquals('null://', (string) $t);
}
}

View File

@ -27,20 +27,15 @@ class SendmailTransportFactoryTest extends TransportFactoryTestCase
public function supportsProvider(): iterable
{
yield [
new Dsn('smtp', 'sendmail'),
new Dsn('sendmail+smtp', 'default'),
true,
];
yield [
new Dsn('smtp', 'example.com'),
false,
];
}
public function createProvider(): iterable
{
yield [
new Dsn('smtp', 'sendmail'),
new Dsn('sendmail+smtp', 'default'),
new SendmailTransport(null, $this->getDispatcher(), $this->getLogger()),
];
}
@ -48,8 +43,8 @@ class SendmailTransportFactoryTest extends TransportFactoryTestCase
public function unsupportedSchemeProvider(): iterable
{
yield [
new Dsn('http', 'sendmail'),
'The "http" scheme is not supported for mailer "sendmail". Supported schemes are: "smtp".',
new Dsn('sendmail+http', 'default'),
'The "sendmail+http" scheme is not supported. Supported schemes for mailer "sendmail" are: "sendmail", "sendmail+smtp".',
];
}
}

View File

@ -64,7 +64,7 @@ class EsmtpTransportFactoryTest extends TransportFactoryTestCase
$transport = new EsmtpTransport('example.com', 465, true, $eventDispatcher, $logger);
yield [
new Dsn('smtp', 'example.com', '', '', 465),
new Dsn('smtps', 'example.com', '', '', 465),
$transport,
];
}

View File

@ -24,6 +24,8 @@ use Symfony\Contracts\HttpClient\ResponseInterface;
*/
abstract class AbstractHttpTransport extends AbstractTransport
{
protected $host;
protected $port;
protected $client;
public function __construct(HttpClientInterface $client = null, EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null)
@ -40,6 +42,26 @@ abstract class AbstractHttpTransport extends AbstractTransport
parent::__construct($dispatcher, $logger);
}
/**
* @return $this
*/
public function setHost(?string $host)
{
$this->host = $host;
return $this;
}
/**
* @return $this
*/
public function setPort(?int $port)
{
$this->port = $port;
return $this;
}
abstract protected function doSendHttp(SentMessage $message): ResponseInterface;
protected function doSend(SentMessage $message): void

View File

@ -32,6 +32,13 @@ abstract class AbstractTransportFactory implements TransportFactoryInterface
$this->logger = $logger;
}
public function supports(Dsn $dsn): bool
{
return \in_array($dsn->getScheme(), $this->getSupportedSchemes());
}
abstract protected function getSupportedSchemes(): array;
protected function getUser(Dsn $dsn): string
{
$user = $dsn->getUser();

View File

@ -26,6 +26,6 @@ final class NullTransport extends AbstractTransport
public function __toString(): string
{
return 'smtp://null';
return 'null://';
}
}

View File

@ -20,15 +20,15 @@ final class NullTransportFactory extends AbstractTransportFactory
{
public function create(Dsn $dsn): TransportInterface
{
if ('smtp' === $dsn->getScheme()) {
if ('null' === $dsn->getScheme()) {
return new NullTransport($this->dispatcher, $this->logger);
}
throw new UnsupportedSchemeException($dsn, ['smtp']);
throw new UnsupportedSchemeException($dsn, 'null', $this->getSupportedSchemes());
}
public function supports(Dsn $dsn): bool
protected function getSupportedSchemes(): array
{
return 'null' === $dsn->getHost();
return ['null'];
}
}

View File

@ -20,15 +20,15 @@ final class SendmailTransportFactory extends AbstractTransportFactory
{
public function create(Dsn $dsn): TransportInterface
{
if ('smtp' === $dsn->getScheme()) {
if ('sendmail+smtp' === $dsn->getScheme() || 'sendmail' === $dsn->getScheme()) {
return new SendmailTransport(null, $this->dispatcher, $this->logger);
}
throw new UnsupportedSchemeException($dsn, ['smtp']);
throw new UnsupportedSchemeException($dsn, 'sendmail', $this->getSupportedSchemes());
}
public function supports(Dsn $dsn): bool
protected function getSupportedSchemes(): array
{
return 'sendmail' === $dsn->getHost();
return ['sendmail', 'sendmail+smtp'];
}
}

View File

@ -39,8 +39,8 @@ final class EsmtpTransportFactory extends AbstractTransportFactory
return $transport;
}
public function supports(Dsn $dsn): bool
protected function getSupportedSchemes(): array
{
return 'smtp' === $dsn->getScheme() || 'smtps' === $dsn->getScheme();
return ['smtp', 'smtps'];
}
}