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