plugins.rst 14KB

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