LoadBalancedTransport.php 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. <?php
  2. /*
  3. * This file is part of SwiftMailer.
  4. * (c) 2004-2009 Chris Corbyn
  5. *
  6. * For the full copyright and license information, please view the LICENSE
  7. * file that was distributed with this source code.
  8. */
  9. /**
  10. * Redundantly and rotationally uses several Transports when sending.
  11. *
  12. * @author Chris Corbyn
  13. */
  14. class Swift_Transport_LoadBalancedTransport implements Swift_Transport
  15. {
  16. /**
  17. * Transports which are deemed useless.
  18. *
  19. * @var Swift_Transport[]
  20. */
  21. private $deadTransports = [];
  22. /**
  23. * The Transports which are used in rotation.
  24. *
  25. * @var Swift_Transport[]
  26. */
  27. protected $transports = [];
  28. /**
  29. * The Transport used in the last successful send operation.
  30. *
  31. * @var Swift_Transport
  32. */
  33. protected $lastUsedTransport = null;
  34. // needed as __construct is called from elsewhere explicitly
  35. public function __construct()
  36. {
  37. }
  38. /**
  39. * Set $transports to delegate to.
  40. *
  41. * @param Swift_Transport[] $transports
  42. */
  43. public function setTransports(array $transports)
  44. {
  45. $this->transports = $transports;
  46. $this->deadTransports = [];
  47. }
  48. /**
  49. * Get $transports to delegate to.
  50. *
  51. * @return Swift_Transport[]
  52. */
  53. public function getTransports()
  54. {
  55. return array_merge($this->transports, $this->deadTransports);
  56. }
  57. /**
  58. * Get the Transport used in the last successful send operation.
  59. *
  60. * @return Swift_Transport
  61. */
  62. public function getLastUsedTransport()
  63. {
  64. return $this->lastUsedTransport;
  65. }
  66. /**
  67. * Test if this Transport mechanism has started.
  68. *
  69. * @return bool
  70. */
  71. public function isStarted()
  72. {
  73. return \count($this->transports) > 0;
  74. }
  75. /**
  76. * Start this Transport mechanism.
  77. */
  78. public function start()
  79. {
  80. $this->transports = array_merge($this->transports, $this->deadTransports);
  81. }
  82. /**
  83. * Stop this Transport mechanism.
  84. */
  85. public function stop()
  86. {
  87. foreach ($this->transports as $transport) {
  88. $transport->stop();
  89. }
  90. }
  91. /**
  92. * {@inheritdoc}
  93. */
  94. public function ping()
  95. {
  96. foreach ($this->transports as $transport) {
  97. if (!$transport->ping()) {
  98. $this->killCurrentTransport();
  99. }
  100. }
  101. return \count($this->transports) > 0;
  102. }
  103. /**
  104. * Send the given Message.
  105. *
  106. * Recipient/sender data will be retrieved from the Message API.
  107. * The return value is the number of recipients who were accepted for delivery.
  108. *
  109. * @param string[] $failedRecipients An array of failures by-reference
  110. *
  111. * @return int
  112. */
  113. public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null)
  114. {
  115. $maxTransports = \count($this->transports);
  116. $sent = 0;
  117. $this->lastUsedTransport = null;
  118. for ($i = 0; $i < $maxTransports
  119. && $transport = $this->getNextTransport(); ++$i) {
  120. try {
  121. if (!$transport->isStarted()) {
  122. $transport->start();
  123. }
  124. if ($sent = $transport->send($message, $failedRecipients)) {
  125. $this->lastUsedTransport = $transport;
  126. break;
  127. }
  128. } catch (Swift_TransportException $e) {
  129. $this->killCurrentTransport();
  130. }
  131. }
  132. if (0 == \count($this->transports)) {
  133. throw new Swift_TransportException('All Transports in LoadBalancedTransport failed, or no Transports available');
  134. }
  135. return $sent;
  136. }
  137. /**
  138. * Register a plugin.
  139. */
  140. public function registerPlugin(Swift_Events_EventListener $plugin)
  141. {
  142. foreach ($this->transports as $transport) {
  143. $transport->registerPlugin($plugin);
  144. }
  145. }
  146. /**
  147. * Rotates the transport list around and returns the first instance.
  148. *
  149. * @return Swift_Transport
  150. */
  151. protected function getNextTransport()
  152. {
  153. if ($next = array_shift($this->transports)) {
  154. $this->transports[] = $next;
  155. }
  156. return $next;
  157. }
  158. /**
  159. * Tag the currently used (top of stack) transport as dead/useless.
  160. */
  161. protected function killCurrentTransport()
  162. {
  163. if ($transport = array_pop($this->transports)) {
  164. try {
  165. $transport->stop();
  166. } catch (Exception $e) {
  167. }
  168. $this->deadTransports[] = $transport;
  169. }
  170. }
  171. }