1Plugins 2======= 3 4Plugins exist to extend, or modify the behaviour of Swift Mailer. They respond 5to Events that are fired within the Transports during sending. 6 7There are a number of Plugins provided as part of the base Swift Mailer package 8and they all follow a common interface to respond to Events fired within the 9library. Interfaces are provided to "listen" to each type of Event fired and to 10act as desired when a listened-to Event occurs. 11 12Although several plugins are provided with Swift Mailer out-of-the-box, the 13Events system has been specifically designed to make it easy for experienced 14object-oriented developers to write their own plugins in order to achieve 15goals that may not be possible with the base library. 16 17AntiFlood Plugin 18---------------- 19 20Many SMTP servers have limits on the number of messages that may be sent during 21any single SMTP connection. The AntiFlood plugin provides a way to stay within 22this limit while still managing a large number of emails. 23 24A typical limit for a single connection is 100 emails. If the server you 25connect to imposes such a limit, it expects you to disconnect after that number 26of emails has been sent. You could manage this manually within a loop, but the 27AntiFlood plugin provides the necessary wrapper code so that you don't need to 28worry about this logic. 29 30Regardless of limits imposed by the server, it's usually a good idea to be 31conservative with the resources of the SMTP server. Sending will become 32sluggish if the server is being over-used so using the AntiFlood plugin will 33not be a bad idea even if no limits exist. 34 35The AntiFlood plugin's logic is basically to disconnect and the immediately 36re-connect with the SMTP server every X number of emails sent, where X is a 37number you specify to the plugin. 38 39You can also specify a time period in seconds that Swift Mailer should pause 40for between the disconnect/re-connect process. It's a good idea to pause for a 41short time (say 30 seconds every 100 emails) simply to give the SMTP server a 42chance to process its queue and recover some resources. 43 44Using the AntiFlood Plugin 45~~~~~~~~~~~~~~~~~~~~~~~~~~ 46 47The AntiFlood Plugin -- like all plugins -- is added with the Mailer class's 48``registerPlugin()`` method. It takes two constructor parameters: the number of 49emails to pause after, and optionally the number of seconds to pause for. 50 51When Swift Mailer sends messages it will count the number of messages that have 52been sent since the last re-connect. Once the number hits your specified 53threshold it will disconnect and re-connect, optionally pausing for a specified 54amount of time:: 55 56 // Create the Mailer using any Transport 57 $mailer = new Swift_Mailer( 58 new Swift_SmtpTransport('smtp.example.org', 25) 59 ); 60 61 // Use AntiFlood to re-connect after 100 emails 62 $mailer->registerPlugin(new Swift_Plugins_AntiFloodPlugin(100)); 63 64 // And specify a time in seconds to pause for (30 secs) 65 $mailer->registerPlugin(new Swift_Plugins_AntiFloodPlugin(100, 30)); 66 67 // Continue sending as normal 68 for ($lotsOfRecipients as $recipient) { 69 ... 70 71 $mailer->send( ... ); 72 } 73 74Throttler Plugin 75---------------- 76 77If your SMTP server has restrictions in place to limit the rate at which you 78send emails, then your code will need to be aware of this rate-limiting. The 79Throttler plugin makes Swift Mailer run at a rate-limited speed. 80 81Many shared hosts don't open their SMTP servers as a free-for-all. Usually they 82have policies in place (probably to discourage spammers) that only allow you to 83send a fixed number of emails per-hour/day. 84 85The Throttler plugin supports two modes of rate-limiting and with each, you 86will need to do that math to figure out the values you want. The plugin can 87limit based on the number of emails per minute, or the number of 88bytes-transferred per-minute. 89 90Using the Throttler Plugin 91~~~~~~~~~~~~~~~~~~~~~~~~~~ 92 93The Throttler Plugin -- like all plugins -- is added with the Mailer class' 94``registerPlugin()`` method. It has two required constructor parameters that 95tell it how to do its rate-limiting. 96 97When Swift Mailer sends messages it will keep track of the rate at which 98sending messages is occurring. If it realises that sending is happening too 99fast, it will cause your program to ``sleep()`` for enough time to average out 100the rate:: 101 102 // Create the Mailer using any Transport 103 $mailer = new Swift_Mailer( 104 new Swift_SmtpTransport('smtp.example.org', 25) 105 ); 106 107 // Rate limit to 100 emails per-minute 108 $mailer->registerPlugin(new Swift_Plugins_ThrottlerPlugin( 109 100, Swift_Plugins_ThrottlerPlugin::MESSAGES_PER_MINUTE 110 )); 111 112 // Rate limit to 10MB per-minute 113 $mailer->registerPlugin(new Swift_Plugins_ThrottlerPlugin( 114 1024 * 1024 * 10, Swift_Plugins_ThrottlerPlugin::BYTES_PER_MINUTE 115 )); 116 117 // Continue sending as normal 118 for ($lotsOfRecipients as $recipient) { 119 ... 120 121 $mailer->send( ... ); 122 } 123 124Logger Plugin 125------------- 126 127The Logger plugins helps with debugging during the process of sending. It can 128help to identify why an SMTP server is rejecting addresses, or any other 129hard-to-find problems that may arise. 130 131The Logger plugin comes in two parts. There's the plugin itself, along with one 132of a number of possible Loggers that you may choose to use. For example, the 133logger may output messages directly in realtime, or it may capture messages in 134an array. 135 136One other notable feature is the way in which the Logger plugin changes 137Exception messages. If Exceptions are being thrown but the error message does 138not provide conclusive information as to the source of the problem (such as an 139ambiguous SMTP error) the Logger plugin includes the entire SMTP transcript in 140the error message so that debugging becomes a simpler task. 141 142There are a few available Loggers included with Swift Mailer, but writing your 143own implementation is incredibly simple and is achieved by creating a short 144class that implements the ``Swift_Plugins_Logger`` interface. 145 146* ``Swift_Plugins_Loggers_ArrayLogger``: Keeps a collection of log messages 147 inside an array. The array content can be cleared or dumped out to the screen. 148 149* ``Swift_Plugins_Loggers_EchoLogger``: Prints output to the screen in 150 realtime. Handy for very rudimentary debug output. 151 152Using the Logger Plugin 153~~~~~~~~~~~~~~~~~~~~~~~ 154 155The Logger Plugin -- like all plugins -- is added with the Mailer class' 156``registerPlugin()`` method. It accepts an instance of ``Swift_Plugins_Logger`` 157in its constructor. 158 159When Swift Mailer sends messages it will keep a log of all the interactions 160with the underlying Transport being used. Depending upon the Logger that has 161been used the behaviour will differ, but all implementations offer a way to get 162the contents of the log:: 163 164 // Create the Mailer using any Transport 165 $mailer = new Swift_Mailer( 166 new Swift_SmtpTransport('smtp.example.org', 25) 167 ); 168 169 // To use the ArrayLogger 170 $logger = new Swift_Plugins_Loggers_ArrayLogger(); 171 $mailer->registerPlugin(new Swift_Plugins_LoggerPlugin($logger)); 172 173 // Or to use the Echo Logger 174 $logger = new Swift_Plugins_Loggers_EchoLogger(); 175 $mailer->registerPlugin(new Swift_Plugins_LoggerPlugin($logger)); 176 177 // Continue sending as normal 178 for ($lotsOfRecipients as $recipient) { 179 ... 180 181 $mailer->send( ... ); 182 } 183 184 // Dump the log contents 185 // NOTE: The EchoLogger dumps in realtime so dump() does nothing for it 186 echo $logger->dump(); 187 188Decorator Plugin 189---------------- 190 191Often there's a need to send the same message to multiple recipients, but with 192tiny variations such as the recipient's name being used inside the message 193body. The Decorator plugin aims to provide a solution for allowing these small 194differences. 195 196The decorator plugin works by intercepting the sending process of Swift Mailer, 197reading the email address in the To: field and then looking up a set of 198replacements for a template. 199 200While the use of this plugin is simple, it is probably the most commonly 201misunderstood plugin due to the way in which it works. The typical mistake 202users make is to try registering the plugin multiple times (once for each 203recipient) -- inside a loop for example. This is incorrect. 204 205The Decorator plugin should be registered just once, but containing the list of 206all recipients prior to sending. It will use this list of recipients to find 207the required replacements during sending. 208 209Using the Decorator Plugin 210~~~~~~~~~~~~~~~~~~~~~~~~~~ 211 212To use the Decorator plugin, simply create an associative array of replacements 213based on email addresses and then use the mailer's ``registerPlugin()`` method 214to add the plugin. 215 216First create an associative array of replacements based on the email addresses 217you'll be sending the message to. 218 219.. note:: 220 221 The replacements array becomes a 2-dimensional array whose keys are the 222 email addresses and whose values are an associative array of replacements 223 for that email address. The curly braces used in this example can be any 224 type of syntax you choose, provided they match the placeholders in your 225 email template:: 226 227 $replacements = []; 228 foreach ($users as $user) { 229 $replacements[$user['email']] = [ 230 '{username}'=>$user['username'], 231 '{resetcode}'=>$user['resetcode'] 232 ]; 233 } 234 235Now create an instance of the Decorator plugin using this array of replacements 236and then register it with the Mailer. Do this only once! 237 238:: 239 240 $decorator = new Swift_Plugins_DecoratorPlugin($replacements); 241 242 $mailer->registerPlugin($decorator); 243 244When you create your message, replace elements in the body (and/or the subject 245line) with your placeholders:: 246 247 $message = (new Swift_Message()) 248 ->setSubject('Important notice for {username}') 249 ->setBody( 250 "Hello {username}, you requested to reset your password.\n" . 251 "Please visit https://example.com/pwreset and use the reset code {resetcode} to set a new password." 252 ) 253 ; 254 255 foreach ($users as $user) { 256 $message->addTo($user['email']); 257 } 258 259When you send this message to each of your recipients listed in your 260``$replacements`` array they will receive a message customized for just 261themselves. For example, the message used above when received may appear like 262this to one user: 263 264.. code-block:: text 265 266 Subject: Important notice for smilingsunshine2009 267 268 Hello smilingsunshine2009, you requested to reset your password. 269 Please visit https://example.com/pwreset and use the reset code 183457 to set a new password. 270 271While another use may receive the message as: 272 273.. code-block:: text 274 275 Subject: Important notice for billy-bo-bob 276 277 Hello billy-bo-bob, you requested to reset your password. 278 Please visit https://example.com/pwreset and use the reset code 539127 to set a new password. 279 280While the decorator plugin provides a means to solve this problem, there are 281various ways you could tackle this problem without the need for a plugin. We're 282trying to come up with a better way ourselves and while we have several 283(obvious) ideas we don't quite have the perfect solution to go ahead and 284implement it. Watch this space. 285 286Providing Your Own Replacements Lookup for the Decorator 287~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 288 289Filling an array with replacements may not be the best solution for providing 290replacement information to the decorator. If you have a more elegant algorithm 291that performs replacement lookups on-the-fly you may provide your own 292implementation. 293 294Providing your own replacements lookup implementation for the Decorator is 295simply a matter of passing an instance of 296``Swift_Plugins_Decorator_Replacements`` to the decorator plugin's constructor, 297rather than passing in an array. 298 299The Replacements interface is very simple to implement since it has just one 300method: ``getReplacementsFor($address)``. 301 302Imagine you want to look up replacements from a database on-the-fly, you might 303provide an implementation that does this. You need to create a small class:: 304 305 class DbReplacements implements Swift_Plugins_Decorator_Replacements { 306 public function getReplacementsFor($address) { 307 global $db; // Your PDO instance with a connection to your database 308 $query = $db->prepare( 309 "SELECT * FROM `users` WHERE `email` = ?" 310 ); 311 312 $query->execute([$address]); 313 314 if ($row = $query->fetch(PDO::FETCH_ASSOC)) { 315 return [ 316 '{username}'=>$row['username'], 317 '{resetcode}'=>$row['resetcode'] 318 ]; 319 } 320 } 321 } 322 323Now all you need to do is pass an instance of your class into the Decorator 324plugin's constructor instead of passing an array:: 325 326 $decorator = new Swift_Plugins_DecoratorPlugin(new DbReplacements()); 327 328 $mailer->registerPlugin($decorator); 329 330For each message sent, the plugin will call your class' 331``getReplacementsFor()`` method to find the array of replacements it needs. 332 333.. note:: 334 335 If your lookup algorithm is case sensitive, you should transform the 336 ``$address`` argument as appropriate -- for example by passing it through 337 ``strtolower()``. 338