AbstractTransport.php 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  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\Transport;
  11. use Psr\EventDispatcher\EventDispatcherInterface;
  12. use Psr\Log\LoggerInterface;
  13. use Psr\Log\NullLogger;
  14. use Symfony\Bridge\Twig\Mime\TemplatedEmail;
  15. use Symfony\Component\Mailer\Envelope;
  16. use Symfony\Component\Mailer\Event\FailedMessageEvent;
  17. use Symfony\Component\Mailer\Event\MessageEvent;
  18. use Symfony\Component\Mailer\Event\SentMessageEvent;
  19. use Symfony\Component\Mailer\Exception\LogicException;
  20. use Symfony\Component\Mailer\SentMessage;
  21. use Symfony\Component\Mime\Address;
  22. use Symfony\Component\Mime\BodyRendererInterface;
  23. use Symfony\Component\Mime\RawMessage;
  24. /**
  25. * @author Fabien Potencier <fabien@symfony.com>
  26. */
  27. abstract class AbstractTransport implements TransportInterface
  28. {
  29. private LoggerInterface $logger;
  30. private float $rate = 0;
  31. private float $lastSent = 0;
  32. public function __construct(
  33. private ?EventDispatcherInterface $dispatcher = null,
  34. ?LoggerInterface $logger = null,
  35. ) {
  36. $this->logger = $logger ?? new NullLogger();
  37. }
  38. /**
  39. * Sets the maximum number of messages to send per second (0 to disable).
  40. *
  41. * @return $this
  42. */
  43. public function setMaxPerSecond(float $rate): static
  44. {
  45. if (0 >= $rate) {
  46. $rate = 0;
  47. }
  48. $this->rate = $rate;
  49. $this->lastSent = 0;
  50. return $this;
  51. }
  52. public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage
  53. {
  54. $message = clone $message;
  55. $envelope = null !== $envelope ? clone $envelope : Envelope::create($message);
  56. try {
  57. if (!$this->dispatcher) {
  58. $sentMessage = new SentMessage($message, $envelope);
  59. $this->doSend($sentMessage);
  60. return $sentMessage;
  61. }
  62. $event = new MessageEvent($message, $envelope, (string) $this);
  63. $this->dispatcher->dispatch($event);
  64. if ($event->isRejected()) {
  65. return null;
  66. }
  67. $envelope = $event->getEnvelope();
  68. $message = $event->getMessage();
  69. if ($message instanceof TemplatedEmail && !$message->isRendered()) {
  70. throw new LogicException(\sprintf('You must configure a "%s" when a "%s" instance has a text or HTML template set.', BodyRendererInterface::class, get_debug_type($message)));
  71. }
  72. $sentMessage = new SentMessage($message, $envelope);
  73. try {
  74. $this->doSend($sentMessage);
  75. } catch (\Throwable $error) {
  76. $this->dispatcher->dispatch(new FailedMessageEvent($message, $error));
  77. $this->checkThrottling();
  78. throw $error;
  79. }
  80. $this->dispatcher->dispatch(new SentMessageEvent($sentMessage));
  81. return $sentMessage;
  82. } finally {
  83. $this->checkThrottling();
  84. }
  85. }
  86. abstract protected function doSend(SentMessage $message): void;
  87. /**
  88. * @param Address[] $addresses
  89. *
  90. * @return string[]
  91. */
  92. protected function stringifyAddresses(array $addresses): array
  93. {
  94. return array_map(fn (Address $a) => $a->toString(), $addresses);
  95. }
  96. protected function getLogger(): LoggerInterface
  97. {
  98. return $this->logger;
  99. }
  100. private function checkThrottling(): void
  101. {
  102. if (0 == $this->rate) {
  103. return;
  104. }
  105. $sleep = (1 / $this->rate) - (microtime(true) - $this->lastSent);
  106. if (0 < $sleep) {
  107. $this->logger->debug(\sprintf('Email transport "%s" sleeps for %.2f seconds', __CLASS__, $sleep));
  108. usleep((int) ($sleep * 1000000));
  109. }
  110. $this->lastSent = microtime(true);
  111. }
  112. }