18
18
use Symfony \Component \Mailer \Bridge \Mailgun \Transport \MailgunTransportFactory ;
19
19
use Symfony \Component \Mailer \Bridge \Postmark \Transport \PostmarkTransportFactory ;
20
20
use Symfony \Component \Mailer \Bridge \Sendgrid \Transport \SendgridTransportFactory ;
21
+ use Symfony \Component \Mailer \Exception \InvalidArgumentException ;
21
22
use Symfony \Component \Mailer \Exception \UnsupportedHostException ;
22
23
use Symfony \Component \Mailer \Transport \Dsn ;
23
24
use Symfony \Component \Mailer \Transport \FailoverTransport ;
@@ -82,17 +83,55 @@ public function fromStrings(array $dsns): Transports
82
83
83
84
public function fromString (string $ dsn ): TransportInterface
84
85
{
85
- $ dsns = preg_split ( ' /\s++\|\|\s++/ ' , $ dsn );
86
- if (\count ( $ dsns ) > 1 ) {
87
- return new FailoverTransport ( $ this -> createFromDsns ( $ dsns ));
86
+ list ( $ transport , $ offset ) = $ this -> parseDsn ( $ dsn );
87
+ if ($ offset !== \strlen ( $ dsn ) ) {
88
+ throw new InvalidArgumentException ( sprintf ( ' The DSN has some garbage at the end: %s. ' , substr ( $ dsn , $ offset ) ));
88
89
}
89
90
90
- $ dsns = preg_split ('/\s++&&\s++/ ' , $ dsn );
91
- if (\count ($ dsns ) > 1 ) {
92
- return new RoundRobinTransport ($ this ->createFromDsns ($ dsns ));
93
- }
91
+ return $ transport ;
92
+ }
93
+
94
+ private function parseDsn (string $ dsn , int $ offset = 0 ): array
95
+ {
96
+ static $ keywords = [
97
+ 'failover ' => FailoverTransport::class,
98
+ 'roundrobin ' => RoundRobinTransport::class,
99
+ ];
100
+
101
+ while (true ) {
102
+ foreach ($ keywords as $ name => $ class ) {
103
+ $ name .= '( ' ;
104
+ if ($ name === substr ($ dsn , $ offset , \strlen ($ name ))) {
105
+ $ offset += \strlen ($ name ) - 1 ;
106
+ preg_match ('{\(([^()]|(?R))*\)}A ' , $ dsn , $ matches , 0 , $ offset );
107
+ if (!isset ($ matches [0 ])) {
108
+ continue ;
109
+ }
110
+
111
+ ++$ offset ;
112
+ $ args = [];
113
+ while (true ) {
114
+ list ($ arg , $ offset ) = $ this ->parseDsn ($ dsn , $ offset );
115
+ $ args [] = $ arg ;
116
+ if (\strlen ($ dsn ) === $ offset ) {
117
+ break ;
118
+ }
119
+ ++$ offset ;
120
+ if (') ' === $ dsn [$ offset - 1 ]) {
121
+ break ;
122
+ }
123
+ }
124
+
125
+ return [new $ class ($ args ), $ offset ];
126
+ }
127
+ }
128
+
129
+ if ($ pos = strcspn ($ dsn , ' ) ' , $ offset )) {
130
+ return [$ this ->fromDsnObject (Dsn::fromString (substr ($ dsn , $ offset , $ pos ))), $ offset + $ pos ];
131
+ }
94
132
95
- return $ this ->fromDsnObject (Dsn::fromString ($ dsn ));
133
+ return [$ this ->fromDsnObject (Dsn::fromString (substr ($ dsn , $ offset ))), \strlen ($ dsn )];
134
+ }
96
135
}
97
136
98
137
public function fromDsnObject (Dsn $ dsn ): TransportInterface
@@ -106,21 +145,6 @@ public function fromDsnObject(Dsn $dsn): TransportInterface
106
145
throw new UnsupportedHostException ($ dsn );
107
146
}
108
147
109
- /**
110
- * @param string[] $dsns
111
- *
112
- * @return TransportInterface[]
113
- */
114
- private function createFromDsns (array $ dsns ): array
115
- {
116
- $ transports = [];
117
- foreach ($ dsns as $ dsn ) {
118
- $ transports [] = $ this ->fromDsnObject (Dsn::fromString ($ dsn ));
119
- }
120
-
121
- return $ transports ;
122
- }
123
-
124
148
private static function getDefaultFactories (EventDispatcherInterface $ dispatcher = null , HttpClientInterface $ client = null , LoggerInterface $ logger = null ): iterable
125
149
{
126
150
foreach (self ::FACTORY_CLASSES as $ factoryClass ) {
0 commit comments