1<?php 2 3/** 4 * Swift Mailer Multiple Redundant Cycling Connection component. 5 * Please read the LICENSE file 6 * @author Chris Corbyn <chris@w3style.co.uk> 7 * @package Swift_Connection 8 * @license GNU Lesser General Public License 9 */ 10 11require_once dirname(__FILE__) . "/../ClassLoader.php"; 12Swift_ClassLoader::load("Swift_ConnectionBase"); 13 14/** 15 * Swift Rotator Connection 16 * Switches through each connection in turn after sending each message 17 * @package Swift_Connection 18 * @author Chris Corbyn <chris@w3style.co.uk> 19 */ 20class Swift_Connection_Rotator extends Swift_ConnectionBase 21{ 22 /** 23 * The list of available connections 24 * @var array 25 */ 26 protected $connections = array(); 27 /** 28 * The id of the active connection 29 * @var int 30 */ 31 protected $active = null; 32 /** 33 * Contains a list of any connections which were tried but found to be dead 34 * @var array 35 */ 36 protected $dead = array(); 37 38 /** 39 * Constructor 40 */ 41 public function __construct($connections=array()) 42 { 43 foreach ($connections as $id => $conn) 44 { 45 $this->addConnection($connections[$id], $id); 46 } 47 } 48 /** 49 * Add a connection to the list of options 50 * @param Swift_Connection An instance of the connection 51 */ 52 public function addConnection(Swift_Connection $connection) 53 { 54 $log = Swift_LogContainer::getLog(); 55 if ($log->hasLevel(Swift_Log::LOG_EVERYTHING)) 56 { 57 $log->add("Adding new connection of type '" . get_class($connection) . "' to rotator."); 58 } 59 $this->connections[] = $connection; 60 } 61 /** 62 * Rotate to the next working connection 63 * @throws Swift_ConnectionException If no connections are available 64 */ 65 public function nextConnection() 66 { 67 $log = Swift_LogContainer::getLog(); 68 if ($log->hasLevel(Swift_Log::LOG_EVERYTHING)) 69 { 70 $log->add(" <==> Rotating connection."); 71 } 72 73 $total = count($this->connections); 74 $start = $this->active === null ? 0 : ($this->active + 1); 75 if ($start >= $total) $start = 0; 76 77 $fail_messages = array(); 78 for ($id = $start; $id < $total; $id++) 79 { 80 if (in_array($id, $this->dead)) continue; //The connection was previously found to be useless 81 try { 82 if (!$this->connections[$id]->isAlive()) $this->connections[$id]->start(); 83 if ($this->connections[$id]->isAlive()) 84 { 85 $this->active = $id; 86 return true; 87 } 88 else 89 { 90 $this->dead[] = $id; 91 $this->connections[$id]->stop(); 92 throw new Swift_ConnectionException("The connection started but reported that it was not active"); 93 } 94 } catch (Swift_ConnectionException $e) { 95 $fail_messages[] = $id . ": " . $e->getMessage(); 96 } 97 } 98 99 $failure = implode("<br />", $fail_messages); 100 throw new Swift_ConnectionException("No connections were started.<br />" . $failure); 101 } 102 /** 103 * Read a full response from the buffer 104 * @return string 105 * @throws Swift_ConnectionException Upon failure to read 106 */ 107 public function read() 108 { 109 if ($this->active === null) 110 { 111 throw new Swift_ConnectionException("None of the connections set have been started"); 112 } 113 return $this->connections[$this->active]->read(); 114 } 115 /** 116 * Write a command to the server (leave off trailing CRLF) 117 * @param string The command to send 118 * @throws Swift_ConnectionException Upon failure to write 119 */ 120 public function write($command, $end="\r\n") 121 { 122 if ($this->active === null) 123 { 124 throw new Swift_ConnectionException("None of the connections set have been started"); 125 } 126 return $this->connections[$this->active]->write($command, $end); 127 } 128 /** 129 * Try to start the connection 130 * @throws Swift_ConnectionException Upon failure to start 131 */ 132 public function start() 133 { 134 if ($this->active === null) $this->nextConnection(); 135 } 136 /** 137 * Try to close the connection 138 * @throws Swift_ConnectionException Upon failure to close 139 */ 140 public function stop() 141 { 142 foreach ($this->connections as $id => $conn) 143 { 144 if ($this->connections[$id]->isAlive()) $this->connections[$id]->stop(); 145 } 146 $this->active = null; 147 } 148 /** 149 * Check if the current connection is alive 150 * @return boolean 151 */ 152 public function isAlive() 153 { 154 return (($this->active !== null) && $this->connections[$this->active]->isAlive()); 155 } 156 /** 157 * Get the ID of the active connection 158 * @return int 159 */ 160 public function getActive() 161 { 162 return $this->active; 163 } 164 /** 165 * Call the current connection's postConnect() method 166 */ 167 public function postConnect(Swift $instance) 168 { 169 Swift_ClassLoader::load("Swift_Plugin_ConnectionRotator"); 170 if (!$instance->getPlugin("_ROTATOR")) $instance->attachPlugin(new Swift_Plugin_ConnectionRotator(), "_ROTATOR"); 171 $this->connections[$this->active]->postConnect($instance); 172 } 173 /** 174 * Call the current connection's setExtension() method 175 */ 176 public function setExtension($extension, $attributes=array()) 177 { 178 $this->connections[$this->active]->setExtension($extension, $attributes); 179 } 180 /** 181 * Call the current connection's hasExtension() method 182 */ 183 public function hasExtension($name) 184 { 185 return $this->connections[$this->active]->hasExtension($name); 186 } 187 /** 188 * Call the current connection's getAttributes() method 189 */ 190 public function getAttributes($name) 191 { 192 return $this->connections[$this->active]->getAttributes($name); 193 } 194} 195