1818use Symfony \Component \Mailer \Bridge \Mailgun \Transport \MailgunTransportFactory ;
1919use Symfony \Component \Mailer \Bridge \Postmark \Transport \PostmarkTransportFactory ;
2020use Symfony \Component \Mailer \Bridge \Sendgrid \Transport \SendgridTransportFactory ;
21+ use Symfony \Component \Mailer \Exception \InvalidArgumentException ;
2122use Symfony \Component \Mailer \Exception \UnsupportedHostException ;
2223use Symfony \Component \Mailer \Transport \Dsn ;
2324use Symfony \Component \Mailer \Transport \FailoverTransport ;
@@ -82,17 +83,59 @@ public function fromStrings(array $dsns): Transports
8283
8384 public function fromString (string $ dsn ): TransportInterface
8485 {
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 ) ));
8889 }
8990
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 (preg_match ('{(\w+)\(}A ' , $ dsn , $ matches , 0 , $ offset )) {
130+ throw new InvalidArgumentException (sprintf ('The "%s" keyword is not valid (valid ones are "%s"), ' , $ matches [1 ], implode ('", " ' , array_keys ($ keywords ))));
131+ }
132+
133+ if ($ pos = strcspn ($ dsn , ' ) ' , $ offset )) {
134+ return [$ this ->fromDsnObject (Dsn::fromString (substr ($ dsn , $ offset , $ pos ))), $ offset + $ pos ];
135+ }
94136
95- return $ this ->fromDsnObject (Dsn::fromString ($ dsn ));
137+ return [$ this ->fromDsnObject (Dsn::fromString (substr ($ dsn , $ offset ))), \strlen ($ dsn )];
138+ }
96139 }
97140
98141 public function fromDsnObject (Dsn $ dsn ): TransportInterface
@@ -106,21 +149,6 @@ public function fromDsnObject(Dsn $dsn): TransportInterface
106149 throw new UnsupportedHostException ($ dsn );
107150 }
108151
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-
124152 private static function getDefaultFactories (EventDispatcherInterface $ dispatcher = null , HttpClientInterface $ client = null , LoggerInterface $ logger = null ): iterable
125153 {
126154 foreach (self ::FACTORY_CLASSES as $ factoryClass ) {
0 commit comments