Skip to content

Commit

Permalink
[Mailer] Change the syntax for DSNs using failover or roundrobin
Browse files Browse the repository at this point in the history
  • Loading branch information
fabpot committed Sep 6, 2019
1 parent b7371ea commit aec11ea
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 25 deletions.
12 changes: 12 additions & 0 deletions src/Symfony/Component/Mailer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ CHANGELOG
4.4.0
-----

* [BC BREAK] changed the syntax for failover and roundrobin DSNs

Before:

dummy://a || dummy://b (for failover)
dummy://a && dummy://b (for roundrobin)

After:

failover(dummy://a dummy://b)
roundrobin(dummy://a dummy://b)

* added support for multiple transports on a `Mailer` instance
* [BC BREAK] removed the `auth_mode` DSN option (it is now always determined automatically)
* STARTTLS cannot be enabled anymore (it is used automatically if TLS is disabled and the server supports STARTTLS)
Expand Down
9 changes: 7 additions & 2 deletions src/Symfony/Component/Mailer/Tests/TransportTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,19 @@ public function fromStringProvider(): iterable
];

yield 'failover transport' => [
'dummy://a || dummy://b',
'failover(dummy://a dummy://b)',
new FailoverTransport([$transportA, $transportB]),
];

yield 'round robin transport' => [
'dummy://a && dummy://b',
'roundrobin(dummy://a dummy://b)',
new RoundRobinTransport([$transportA, $transportB]),
];

yield 'mixed transport' => [
'roundrobin(dummy://a failover(dummy://b dummy://a) dummy://b)',
new RoundRobinTransport([$transportA, new FailoverTransport([$transportB, $transportA]), $transportB]),
];
}
}

Expand Down
70 changes: 47 additions & 23 deletions src/Symfony/Component/Mailer/Transport.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Symfony\Component\Mailer\Bridge\Mailgun\Transport\MailgunTransportFactory;
use Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkTransportFactory;
use Symfony\Component\Mailer\Bridge\Sendgrid\Transport\SendgridTransportFactory;
use Symfony\Component\Mailer\Exception\InvalidArgumentException;
use Symfony\Component\Mailer\Exception\UnsupportedHostException;
use Symfony\Component\Mailer\Transport\Dsn;
use Symfony\Component\Mailer\Transport\FailoverTransport;
Expand Down Expand Up @@ -82,17 +83,55 @@ public function fromStrings(array $dsns): Transports

public function fromString(string $dsn): TransportInterface
{
$dsns = preg_split('/\s++\|\|\s++/', $dsn);
if (\count($dsns) > 1) {
return new FailoverTransport($this->createFromDsns($dsns));
list($transport, $offset) = $this->parseDsn($dsn);
if ($offset !== \strlen($dsn)) {
throw new InvalidArgumentException(sprintf('The DSN has some garbage at the end: %s.', substr($dsn, $offset)));
}

$dsns = preg_split('/\s++&&\s++/', $dsn);
if (\count($dsns) > 1) {
return new RoundRobinTransport($this->createFromDsns($dsns));
}
return $transport;
}

private function parseDsn(string $dsn, int $offset = 0): array
{
static $maps = [
'failover' => FailoverTransport::class,
'roundrobin' => RoundRobinTransport::class,
];

while (true) {
foreach ($maps as $name => $class) {
$name .= '(';
if ($name === substr($dsn, $offset, \strlen($name))) {
$offset += \strlen($name) - 1;
preg_match('{\(([^()]|(?R))*\)}A', $dsn, $matches, 0, $offset);
if (!isset($matches[0])) {
continue;
}

++$offset;
$args = [];
while (true) {
list($arg, $offset) = $this->parseDsn($dsn, $offset);
$args[] = $arg;
if (\strlen($dsn) === $offset) {
break;
}
++$offset;
if (')' === $dsn[$offset - 1]) {
break;
}
}

return [new $class($args), $offset];
}
}

if ($pos = strcspn($dsn, ' )', $offset)) {
return [$this->fromDsnObject(Dsn::fromString(substr($dsn, $offset, $pos))), $offset + $pos];
}

return $this->fromDsnObject(Dsn::fromString($dsn));
return [$this->fromDsnObject(Dsn::fromString(substr($dsn, $offset))), \strlen($dsn)];
}
}

public function fromDsnObject(Dsn $dsn): TransportInterface
Expand All @@ -106,21 +145,6 @@ public function fromDsnObject(Dsn $dsn): TransportInterface
throw new UnsupportedHostException($dsn);
}

/**
* @param string[] $dsns
*
* @return TransportInterface[]
*/
private function createFromDsns(array $dsns): array
{
$transports = [];
foreach ($dsns as $dsn) {
$transports[] = $this->fromDsnObject(Dsn::fromString($dsn));
}

return $transports;
}

private static function getDefaultFactories(EventDispatcherInterface $dispatcher = null, HttpClientInterface $client = null, LoggerInterface $logger = null): iterable
{
foreach (self::FACTORY_CLASSES as $factoryClass) {
Expand Down

0 comments on commit aec11ea

Please sign in to comment.