Skip to content

Commit 8b504d9

Browse files
committed
[Mailer] Add NativeTransportFactory
1 parent d555112 commit 8b504d9

File tree

5 files changed

+233
-1
lines changed

5 files changed

+233
-1
lines changed

src/Symfony/Bundle/FrameworkBundle/Resources/config/mailer_transports.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkTransportFactory;
1919
use Symfony\Component\Mailer\Bridge\Sendgrid\Transport\SendgridTransportFactory;
2020
use Symfony\Component\Mailer\Transport\AbstractTransportFactory;
21+
use Symfony\Component\Mailer\Transport\NativeTransportFactory;
2122
use Symfony\Component\Mailer\Transport\NullTransportFactory;
2223
use Symfony\Component\Mailer\Transport\SendmailTransportFactory;
2324
use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransportFactory;
@@ -67,5 +68,8 @@
6768
->set('mailer.transport_factory.smtp', EsmtpTransportFactory::class)
6869
->parent('mailer.transport_factory.abstract')
6970
->tag('mailer.transport_factory', ['priority' => -100])
70-
;
71+
72+
->set('mailer.transport_factory.native', NativeTransportFactory::class)
73+
->parent('mailer.transport_factory.abstract')
74+
->tag('mailer.transport_factory');
7175
};
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
<?php
2+
3+
namespace Symfony\Component\Mailer\Tests\Transport;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use Symfony\Component\Mailer\Exception\UnsupportedSchemeException;
7+
use Symfony\Component\Mailer\Transport\Dsn;
8+
use Symfony\Component\Mailer\Transport\NativeTransportFactory;
9+
use Symfony\Component\Mailer\Transport\SendmailTransport;
10+
use Symfony\Component\Mailer\Transport\Smtp\SmtpTransport;
11+
use Symfony\Component\Mailer\Transport\Smtp\Stream\SocketStream;
12+
use Symfony\Component\Mailer\Transport\TransportInterface;
13+
14+
final class NativeTransportFactoryTest extends TestCase
15+
{
16+
public static $fakeConfiguration = [];
17+
18+
public static function setUpBeforeClass(): void
19+
{
20+
parent::setUpBeforeClass();
21+
22+
$namespace = str_replace('\\Tests\\', '\\', __NAMESPACE__);
23+
24+
$current = static::class;
25+
26+
$eval = <<<EOT
27+
namespace $namespace;
28+
29+
function ini_get(\$key)
30+
{
31+
\$vals = \\$current::\$fakeConfiguration;
32+
return \$vals[\$key] ?? '';
33+
}
34+
EOT;
35+
eval($eval);
36+
}
37+
38+
public function provideCreateWithNotSupportedScheme(): \Generator
39+
{
40+
yield ['sendmail://default'];
41+
42+
if (PHP_OS_FAMILY != 'Windows') {
43+
yield ['native+smtp://default'];
44+
}
45+
}
46+
47+
/**
48+
* @dataProvider provideCreateWithNotSupportedScheme
49+
*/
50+
public function testCreateWithNotSupportedScheme(string $dsn)
51+
{
52+
$this->expectException(UnsupportedSchemeException::class);
53+
$this->expectExceptionMessageRegExp('#The ".*" scheme is not supported#');
54+
55+
$sut = new NativeTransportFactory();
56+
$sut->create(Dsn::fromString($dsn));
57+
}
58+
59+
public function provideCreateSendmailWithNoSendmailPath(): \Generator
60+
{
61+
if ('Linux' === PHP_OS_FAMILY) {
62+
yield ['native://default'];
63+
}
64+
65+
yield ['native+sendmail://default'];
66+
}
67+
68+
/**
69+
* @dataProvider provideCreateSendmailWithNoSendmailPath
70+
*/
71+
public function testCreateSendmailWithNoSendmailPath(string $dsn)
72+
{
73+
$this->expectException(\Exception::class);
74+
$this->expectExceptionMessage('sendmail_path is not configured');
75+
76+
$sut = new NativeTransportFactory();
77+
$sut->create(Dsn::fromString($dsn));
78+
}
79+
80+
public function provideCreateSendmailWithNoHostOrNoPort(): array
81+
{
82+
return [
83+
['native://default', '', ''],
84+
['native+smtp://default', '/usr/sbin/sendmail -t -i', ''],
85+
['native://default', '', 'localhost'],
86+
];
87+
}
88+
89+
/**
90+
* @requires OSFAMILY Windows
91+
*
92+
* @dataProvider provideCreateSendmailWithNoHostOrNoPort
93+
*/
94+
public function testCreateSendmailWithNoHostOrNoPort(string $dsn, string $sendmaiPath, string $smtp)
95+
{
96+
$this->expectException(\Exception::class);
97+
$this->expectExceptionMessage('smtp or smtp_port is not configured');
98+
99+
self::$fakeConfiguration = [
100+
'sendmail_path' => $sendmaiPath,
101+
'smtp' => $smtp,
102+
];
103+
104+
$sut = new NativeTransportFactory();
105+
$sut->create(Dsn::fromString($dsn));
106+
}
107+
108+
public function provideCreate(): \Generator
109+
{
110+
yield ['native://default', '/usr/sbin/sendmail -t -i', '', '', new SendmailTransport('/usr/sbin/sendmail -t -i')];
111+
yield ['native+sendmail://default', '/usr/sbin/sendmail -t -i', '', '', new SendmailTransport('/usr/sbin/sendmail -t -i')];
112+
113+
if ('Windows' === PHP_OS_FAMILY) {
114+
$socketStream = new SocketStream();
115+
$socketStream->setHost('myhost.tld');
116+
$socketStream->setPort(25);
117+
$socketStream->disableTls();
118+
yield ['native://default', '', 'myhost.tld', '25', new SmtpTransport($socketStream)];
119+
yield ['native+smtp://default', '/usr/sbin/sendmail -t -i', 'myhost.tld', '25', new SmtpTransport($socketStream)];
120+
121+
$socketStreamTls = new SocketStream();
122+
$socketStreamTls->setHost('myhost.tld');
123+
$socketStreamTls->setPort(465);
124+
yield ['native://default', '', 'myhost.tld', '465', new SmtpTransport($socketStreamTls)];
125+
}
126+
}
127+
128+
/**
129+
* @dataProvider provideCreate
130+
*/
131+
public function testCreate(string $dsn, string $sendmailPath, string $smtp, string $smtpPort, TransportInterface $expectedTransport)
132+
{
133+
self::$fakeConfiguration = [
134+
'sendmail_path' => $sendmailPath,
135+
'smtp' => $smtp,
136+
'smtp_port' => $smtpPort,
137+
];
138+
139+
$sut = new NativeTransportFactory();
140+
$transport = $sut->create(Dsn::fromString($dsn));
141+
142+
$this->assertEquals($expectedTransport, $transport);
143+
}
144+
}

src/Symfony/Component/Mailer/Transport.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use Symfony\Component\Mailer\Exception\UnsupportedSchemeException;
2323
use Symfony\Component\Mailer\Transport\Dsn;
2424
use Symfony\Component\Mailer\Transport\FailoverTransport;
25+
use Symfony\Component\Mailer\Transport\NativeTransportFactory;
2526
use Symfony\Component\Mailer\Transport\NullTransportFactory;
2627
use Symfony\Component\Mailer\Transport\RoundRobinTransport;
2728
use Symfony\Component\Mailer\Transport\SendmailTransportFactory;
@@ -162,5 +163,7 @@ public static function getDefaultFactories(EventDispatcherInterface $dispatcher
162163
yield new SendmailTransportFactory($dispatcher, $client, $logger);
163164

164165
yield new EsmtpTransportFactory($dispatcher, $client, $logger);
166+
167+
yield new NativeTransportFactory($dispatcher, $client, $logger);
165168
}
166169
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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\Transport;
13+
14+
use Symfony\Component\Mailer\Exception\TransportException;
15+
use Symfony\Component\Mailer\Exception\UnsupportedSchemeException;
16+
use Symfony\Component\Mailer\Transport\Smtp\SmtpTransport;
17+
use Symfony\Component\Mailer\Transport\Smtp\Stream\SocketStream;
18+
19+
/**
20+
* Factory that configures a SendmailTransport using configuration php.ini.
21+
*
22+
* It offers an alternative to SendmailTransportFactory when the configuration it uses
23+
* is not valid on the current environment (sendmail different path or options unavailable)
24+
*
25+
* Supported modes are -bs and -t, with any additional flags desired.
26+
* It is advised to use -bs mode since error reporting with -t mode is not
27+
* possible.
28+
*
29+
* @author Laurent VOULLEMIER <laurent.voullemier@gmail.com>
30+
*/
31+
final class NativeTransportFactory extends AbstractTransportFactory
32+
{
33+
public function create(Dsn $dsn): TransportInterface
34+
{
35+
if (!\in_array($dsn->getScheme(), $this->getSupportedSchemes(), true)) {
36+
throw new UnsupportedSchemeException($dsn, 'native', $this->getSupportedSchemes());
37+
}
38+
39+
$isWindows = '\\' === \DIRECTORY_SEPARATOR;
40+
if ($sendMailPath = ini_get('sendmail_path')) {
41+
if (!$isWindows || 'native+sendmail' === $dsn->getScheme() || 'native' === $dsn->getScheme()) {
42+
return new SendmailTransport($sendMailPath, $this->dispatcher, $this->logger);
43+
}
44+
}
45+
46+
if (!$isWindows || 'native+sendmail' === $dsn->getScheme()) {
47+
throw new TransportException('sendmail_path is not configured in php.ini.');
48+
}
49+
50+
// Only for windows hosts; at this point non-windows
51+
// host have already thrown an exception or returned a transport
52+
$host = ini_get('smtp');
53+
$port = (int) ini_get('smtp_port');
54+
55+
if (!$host || !$port) {
56+
throw new TransportException('smtp or smtp_port is not configured in php.ini.');
57+
}
58+
59+
$socketStream = new SocketStream();
60+
$socketStream->setHost($host);
61+
$socketStream->setPort($port);
62+
if (465 !== $port) {
63+
$socketStream->disableTls();
64+
}
65+
66+
return new SmtpTransport($socketStream, $this->dispatcher, $this->logger);
67+
}
68+
69+
protected function getSupportedSchemes(): array
70+
{
71+
$supportedShemes = ['native', 'native+sendmail'];
72+
73+
if ('\\' === \DIRECTORY_SEPARATOR) {
74+
$supportedShemes[] = 'native+smtp';
75+
}
76+
77+
return $supportedShemes;
78+
}
79+
}

src/Symfony/Component/Mailer/Transport/SendmailTransport.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
* It is advised to use -bs mode since error reporting with -t mode is not
2828
* possible.
2929
*
30+
* Transport can be instanciated through SendmailTransportFactory or NativeTransportFactory
31+
*
3032
* @author Fabien Potencier <fabien@symfony.com>
3133
* @author Chris Corbyn
3234
*/

0 commit comments

Comments
 (0)