Transport.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Mailer;
  11. use Psr\EventDispatcher\EventDispatcherInterface;
  12. use Psr\Log\LoggerInterface;
  13. use Symfony\Component\Mailer\Bridge\AhaSend\Transport\AhaSendTransportFactory;
  14. use Symfony\Component\Mailer\Bridge\Amazon\Transport\SesTransportFactory;
  15. use Symfony\Component\Mailer\Bridge\Azure\Transport\AzureTransportFactory;
  16. use Symfony\Component\Mailer\Bridge\Brevo\Transport\BrevoTransportFactory;
  17. use Symfony\Component\Mailer\Bridge\Google\Transport\GmailTransportFactory;
  18. use Symfony\Component\Mailer\Bridge\Infobip\Transport\InfobipTransportFactory;
  19. use Symfony\Component\Mailer\Bridge\Mailchimp\Transport\MandrillTransportFactory;
  20. use Symfony\Component\Mailer\Bridge\MailerSend\Transport\MailerSendTransportFactory;
  21. use Symfony\Component\Mailer\Bridge\Mailgun\Transport\MailgunTransportFactory;
  22. use Symfony\Component\Mailer\Bridge\Mailjet\Transport\MailjetTransportFactory;
  23. use Symfony\Component\Mailer\Bridge\Mailomat\Transport\MailomatTransportFactory;
  24. use Symfony\Component\Mailer\Bridge\MailPace\Transport\MailPaceTransportFactory;
  25. use Symfony\Component\Mailer\Bridge\Mailtrap\Transport\MailtrapTransportFactory;
  26. use Symfony\Component\Mailer\Bridge\Postal\Transport\PostalTransportFactory;
  27. use Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkTransportFactory;
  28. use Symfony\Component\Mailer\Bridge\Resend\Transport\ResendTransportFactory;
  29. use Symfony\Component\Mailer\Bridge\Scaleway\Transport\ScalewayTransportFactory;
  30. use Symfony\Component\Mailer\Bridge\Sendgrid\Transport\SendgridTransportFactory;
  31. use Symfony\Component\Mailer\Bridge\Sweego\Transport\SweegoTransportFactory;
  32. use Symfony\Component\Mailer\Exception\InvalidArgumentException;
  33. use Symfony\Component\Mailer\Exception\UnsupportedSchemeException;
  34. use Symfony\Component\Mailer\Transport\Dsn;
  35. use Symfony\Component\Mailer\Transport\FailoverTransport;
  36. use Symfony\Component\Mailer\Transport\NativeTransportFactory;
  37. use Symfony\Component\Mailer\Transport\NullTransportFactory;
  38. use Symfony\Component\Mailer\Transport\RoundRobinTransport;
  39. use Symfony\Component\Mailer\Transport\SendmailTransportFactory;
  40. use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransportFactory;
  41. use Symfony\Component\Mailer\Transport\TransportFactoryInterface;
  42. use Symfony\Component\Mailer\Transport\TransportInterface;
  43. use Symfony\Component\Mailer\Transport\Transports;
  44. use Symfony\Contracts\HttpClient\HttpClientInterface;
  45. /**
  46. * @author Fabien Potencier <fabien@symfony.com>
  47. * @author Konstantin Myakshin <molodchick@gmail.com>
  48. */
  49. final class Transport
  50. {
  51. private const FACTORY_CLASSES = [
  52. AhaSendTransportFactory::class,
  53. AzureTransportFactory::class,
  54. BrevoTransportFactory::class,
  55. GmailTransportFactory::class,
  56. InfobipTransportFactory::class,
  57. MailerSendTransportFactory::class,
  58. MailgunTransportFactory::class,
  59. MailjetTransportFactory::class,
  60. MailomatTransportFactory::class,
  61. MailPaceTransportFactory::class,
  62. MandrillTransportFactory::class,
  63. PostalTransportFactory::class,
  64. PostmarkTransportFactory::class,
  65. MailtrapTransportFactory::class,
  66. ResendTransportFactory::class,
  67. ScalewayTransportFactory::class,
  68. SendgridTransportFactory::class,
  69. SesTransportFactory::class,
  70. SweegoTransportFactory::class,
  71. ];
  72. public static function fromDsn(#[\SensitiveParameter] string $dsn, ?EventDispatcherInterface $dispatcher = null, ?HttpClientInterface $client = null, ?LoggerInterface $logger = null): TransportInterface
  73. {
  74. $factory = new self(iterator_to_array(self::getDefaultFactories($dispatcher, $client, $logger)));
  75. return $factory->fromString($dsn);
  76. }
  77. public static function fromDsns(#[\SensitiveParameter] array $dsns, ?EventDispatcherInterface $dispatcher = null, ?HttpClientInterface $client = null, ?LoggerInterface $logger = null): TransportInterface
  78. {
  79. $factory = new self(iterator_to_array(self::getDefaultFactories($dispatcher, $client, $logger)));
  80. return $factory->fromStrings($dsns);
  81. }
  82. /**
  83. * @param TransportFactoryInterface[] $factories
  84. */
  85. public function __construct(
  86. private iterable $factories,
  87. ) {
  88. }
  89. public function fromStrings(#[\SensitiveParameter] array $dsns): Transports
  90. {
  91. $transports = [];
  92. foreach ($dsns as $name => $dsn) {
  93. $transports[$name] = $this->fromString($dsn);
  94. }
  95. return new Transports($transports);
  96. }
  97. public function fromString(#[\SensitiveParameter] string $dsn): TransportInterface
  98. {
  99. [$transport, $offset] = $this->parseDsn($dsn);
  100. if ($offset !== \strlen($dsn)) {
  101. throw new InvalidArgumentException('The mailer DSN has some garbage at the end.');
  102. }
  103. return $transport;
  104. }
  105. private function parseDsn(#[\SensitiveParameter] string $dsn, int $offset = 0): array
  106. {
  107. static $keywords = [
  108. 'failover' => FailoverTransport::class,
  109. 'roundrobin' => RoundRobinTransport::class,
  110. ];
  111. while (true) {
  112. foreach ($keywords as $name => $class) {
  113. $name .= '(';
  114. if ($name === substr($dsn, $offset, \strlen($name))) {
  115. $offset += \strlen($name) - 1;
  116. preg_match('{\(([^()]|(?R))*\)}A', $dsn, $matches, 0, $offset);
  117. if (!isset($matches[0])) {
  118. continue;
  119. }
  120. ++$offset;
  121. $args = [];
  122. while (true) {
  123. [$arg, $offset] = $this->parseDsn($dsn, $offset);
  124. $args[] = $arg;
  125. if (\strlen($dsn) === $offset) {
  126. break;
  127. }
  128. ++$offset;
  129. if (')' === $dsn[$offset - 1]) {
  130. break;
  131. }
  132. }
  133. parse_str(substr($dsn, $offset + 1), $query);
  134. if ($period = $query['retry_period'] ?? 0) {
  135. return [new $class($args, (int) $period), $offset + \strlen('retry_period='.$period) + 1];
  136. }
  137. return [new $class($args), $offset];
  138. }
  139. }
  140. if (preg_match('{(\w+)\(}A', $dsn, $matches, 0, $offset)) {
  141. throw new InvalidArgumentException(\sprintf('The "%s" keyword is not valid (valid ones are "%s"), ', $matches[1], implode('", "', array_keys($keywords))));
  142. }
  143. if ($pos = strcspn($dsn, ' )', $offset)) {
  144. return [$this->fromDsnObject(Dsn::fromString(substr($dsn, $offset, $pos))), $offset + $pos];
  145. }
  146. return [$this->fromDsnObject(Dsn::fromString(substr($dsn, $offset))), \strlen($dsn)];
  147. }
  148. }
  149. public function fromDsnObject(Dsn $dsn): TransportInterface
  150. {
  151. foreach ($this->factories as $factory) {
  152. if ($factory->supports($dsn)) {
  153. return $factory->create($dsn);
  154. }
  155. }
  156. throw new UnsupportedSchemeException($dsn);
  157. }
  158. /**
  159. * @return \Traversable<int, TransportFactoryInterface>
  160. */
  161. public static function getDefaultFactories(?EventDispatcherInterface $dispatcher = null, ?HttpClientInterface $client = null, ?LoggerInterface $logger = null): \Traversable
  162. {
  163. foreach (self::FACTORY_CLASSES as $factoryClass) {
  164. if (class_exists($factoryClass)) {
  165. yield new $factoryClass($dispatcher, $client, $logger);
  166. }
  167. }
  168. yield new NullTransportFactory($dispatcher, $client, $logger);
  169. yield new SendmailTransportFactory($dispatcher, $client, $logger);
  170. yield new EsmtpTransportFactory($dispatcher, $client, $logger);
  171. yield new NativeTransportFactory($dispatcher, $client, $logger);
  172. }
  173. }