summaryrefslogtreecommitdiff
path: root/vendor/swiftmailer/swiftmailer/doc/plugins.rst
blob: 6cec6be26772f04b447553dc5b113dbfa3e4e597 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
Plugins
=======

Plugins are provided with Swift Mailer and can be used to extend the behavior
of the library in situations where using simple class inheritance would be more complex.

AntiFlood Plugin
----------------

Many SMTP servers have limits on the number of messages that may be sent
during any single SMTP connection. The AntiFlood plugin provides a way to stay
within this limit while still managing a large number of emails.

A typical limit for a single connection is 100 emails. If the server you
connect to imposes such a limit, it expects you to disconnect after that
number of emails has been sent. You could manage this manually within a loop,
but the AntiFlood plugin provides the necessary wrapper code so that you don't
need to worry about this logic.

Regardless of limits imposed by the server, it's usually a good idea to be
conservative with the resources of the SMTP server. Sending will become
sluggish if the server is being over-used so using the AntiFlood plugin will
not be a bad idea even if no limits exist.

The AntiFlood plugin's logic is basically to disconnect and the immediately
re-connect with the SMTP server every X number of emails sent, where X is a
number you specify to the plugin.

You can also specify a time period in seconds that Swift Mailer should pause
for between the disconnect/re-connect process. It's a good idea to pause for a
short time (say 30 seconds every 100 emails) simply to give the SMTP server a
chance to process its queue and recover some resources.

Using the AntiFlood Plugin
~~~~~~~~~~~~~~~~~~~~~~~~~~

The AntiFlood Plugin -- like all plugins -- is added with the Mailer class's
``registerPlugin()`` method. It takes two constructor parameters: the number of
emails to pause after, and optionally the number of seconds to pause for.

To use the AntiFlood plugin:

* Create an instance of the Mailer using any Transport you choose.

* Create an instance of the ``Swift_Plugins_AntiFloodPlugin`` class, passing
  in one or two constructor parameters.

* Register the plugin using the Mailer's ``registerPlugin()`` method.

* Continue using Swift Mailer to send messages as normal.

When Swift Mailer sends messages it will count the number of messages that
have been sent since the last re-connect. Once the number hits your specified
threshold it will disconnect and re-connect, optionally pausing for a
specified amount of time.

.. code-block:: php

    require_once 'lib/swift_required.php';

    // Create the Mailer using any Transport
    $mailer = Swift_Mailer::newInstance(
      Swift_SmtpTransport::newInstance('smtp.example.org', 25)
    );

    // Use AntiFlood to re-connect after 100 emails
    $mailer->registerPlugin(new Swift_Plugins_AntiFloodPlugin(100));

    // And specify a time in seconds to pause for (30 secs)
    $mailer->registerPlugin(new Swift_Plugins_AntiFloodPlugin(100, 30));

    // Continue sending as normal
    for ($lotsOfRecipients as $recipient) {
      ...

      $mailer->send( ... );
    }

Throttler Plugin
----------------

If your SMTP server has restrictions in place to limit the rate at which you
send emails, then your code will need to be aware of this rate-limiting. The
Throttler plugin makes Swift Mailer run at a rate-limited speed.

Many shared hosts don't open their SMTP servers as a free-for-all. Usually
they have policies in place (probably to discourage spammers) that only allow
you to send a fixed number of emails per-hour/day.

The Throttler plugin supports two modes of rate-limiting and with each, you
will need to do that math to figure out the values you want. The plugin can
limit based on the number of emails per minute, or the number of
bytes-transferred per-minute.

Using the Throttler Plugin
~~~~~~~~~~~~~~~~~~~~~~~~~~

The Throttler Plugin -- like all plugins -- is added with the Mailer class'
``registerPlugin()`` method. It has two required constructor parameters that
tell it how to do its rate-limiting.

To use the Throttler plugin:

* Create an instance of the Mailer using any Transport you choose.

* Create an instance of the ``Swift_Plugins_ThrottlerPlugin`` class, passing
  the number of emails, or bytes you wish to limit by, along with the mode
  you're using.

* Register the plugin using the Mailer's ``registerPlugin()`` method.

* Continue using Swift Mailer to send messages as normal.

When Swift Mailer sends messages it will keep track of the rate at which sending
messages is occurring. If it realises that sending is happening too fast, it
will cause your program to ``sleep()`` for enough time to average out the rate.

.. code-block:: php

    require_once 'lib/swift_required.php';

    // Create the Mailer using any Transport
    $mailer = Swift_Mailer::newInstance(
      Swift_SmtpTransport::newInstance('smtp.example.org', 25)
    );

    // Rate limit to 100 emails per-minute
    $mailer->registerPlugin(new Swift_Plugins_ThrottlerPlugin(
      100, Swift_Plugins_ThrottlerPlugin::MESSAGES_PER_MINUTE
    ));

    // Rate limit to 10MB per-minute
    $mailer->registerPlugin(new Swift_Plugins_ThrottlerPlugin(
      1024 * 1024 * 10, Swift_Plugins_ThrottlerPlugin::BYTES_PER_MINUTE
    ));

    // Continue sending as normal
    for ($lotsOfRecipients as $recipient) {
      ...

      $mailer->send( ... );
    }

Logger Plugin
-------------

The Logger plugins helps with debugging during the process of sending. It can
help to identify why an SMTP server is rejecting addresses, or any other
hard-to-find problems that may arise.

The Logger plugin comes in two parts. There's the plugin itself, along with
one of a number of possible Loggers that you may choose to use. For example,
the logger may output messages directly in realtime, or it may capture
messages in an array.

One other notable feature is the way in which the Logger plugin changes
Exception messages. If Exceptions are being thrown but the error message does
not provide conclusive information as to the source of the problem (such as an
ambiguous SMTP error) the Logger plugin includes the entire SMTP transcript in
the error message so that debugging becomes a simpler task.

There are a few available Loggers included with Swift Mailer, but writing your
own implementation is incredibly simple and is achieved by creating a short
class that implements the ``Swift_Plugins_Logger`` interface.

* ``Swift_Plugins_Loggers_ArrayLogger``: Keeps a collection of log messages
  inside an array. The array content can be cleared or dumped out to the
  screen.

* ``Swift_Plugins_Loggers_EchoLogger``: Prints output to the screen in
  realtime. Handy for very rudimentary debug output.

Using the Logger Plugin
~~~~~~~~~~~~~~~~~~~~~~~

The Logger Plugin -- like all plugins -- is added with the Mailer class'
``registerPlugin()`` method. It accepts an instance of ``Swift_Plugins_Logger``
in its constructor.

To use the Logger plugin:

* Create an instance of the Mailer using any Transport you choose.

* Create an instance of the a Logger implementation of
  ``Swift_Plugins_Logger``.

* Create an instance of the ``Swift_Plugins_LoggerPlugin`` class, passing the
  created Logger instance to its constructor.

* Register the plugin using the Mailer's ``registerPlugin()`` method.

* Continue using Swift Mailer to send messages as normal.

* Dump the contents of the log with the logger's ``dump()`` method.

When Swift Mailer sends messages it will keep a log of all the interactions
with the underlying Transport being used. Depending upon the Logger that has
been used the behaviour will differ, but all implementations offer a way to
get the contents of the log.

.. code-block:: php

    require_once 'lib/swift_required.php';

    // Create the Mailer using any Transport
    $mailer = Swift_Mailer::newInstance(
     Swift_SmtpTransport::newInstance('smtp.example.org', 25)
    );

    // To use the ArrayLogger
    $logger = new Swift_Plugins_Loggers_ArrayLogger();
    $mailer->registerPlugin(new Swift_Plugins_LoggerPlugin($logger));

    // Or to use the Echo Logger
    $logger = new Swift_Plugins_Loggers_EchoLogger();
    $mailer->registerPlugin(new Swift_Plugins_LoggerPlugin($logger));

    // Continue sending as normal
    for ($lotsOfRecipients as $recipient) {
     ...

     $mailer->send( ... );
    }

    // Dump the log contents
    // NOTE: The EchoLogger dumps in realtime so dump() does nothing for it
    echo $logger->dump();

Decorator Plugin
----------------

Often there's a need to send the same message to multiple recipients, but with
tiny variations such as the recipient's name being used inside the message
body. The Decorator plugin aims to provide a solution for allowing these small
differences.

The decorator plugin works by intercepting the sending process of Swift
Mailer, reading the email address in the To: field and then looking up a set
of replacements for a template.

While the use of this plugin is simple, it is probably the most commonly
misunderstood plugin due to the way in which it works. The typical mistake
users make is to try registering the plugin multiple times (once for each
recipient) -- inside a loop for example. This is incorrect.

The Decorator plugin should be registered just once, but containing the list
of all recipients prior to sending. It will use this list of recipients to
find the required replacements during sending.

Using the Decorator Plugin
~~~~~~~~~~~~~~~~~~~~~~~~~~

To use the Decorator plugin, simply create an associative array of replacements
based on email addresses and then use the mailer's ``registerPlugin()`` method
to add the plugin.

First create an associative array of replacements based on the email addresses
you'll be sending the message to.

.. note::

    The replacements array becomes a 2-dimensional array whose keys are the
    email addresses and whose values are an associative array of replacements
    for that email address. The curly braces used in this example can be any
    type of syntax you choose, provided they match the placeholders in your
    email template.

    .. code-block:: php

        $replacements = array();
        foreach ($users as $user) {
          $replacements[$user['email']] = array(
            '{username}'=>$user['username'],
            '{password}'=>$user['password']
          );
        }

Now create an instance of the Decorator plugin using this array of replacements
and then register it with the Mailer. Do this only once!

.. code-block:: php

    $decorator = new Swift_Plugins_DecoratorPlugin($replacements);

    $mailer->registerPlugin($decorator);

When you create your message, replace elements in the body (and/or the subject
line) with your placeholders.

.. code-block:: php

    $message = Swift_Message::newInstance()
      ->setSubject('Important notice for {username}')
      ->setBody(
        "Hello {username}, we have reset your password to {password}\n" .
        "Please log in and change it at your earliest convenience."
      )
      ;

    foreach ($users as $user) {
      $message->addTo($user['email']);
    }

When you send this message to each of your recipients listed in your
``$replacements`` array they will receive a message customized for just
themselves. For example, the message used above when received may appear like
this to one user:

.. code-block:: text

    Subject: Important notice for smilingsunshine2009

    Hello smilingsunshine2009, we have reset your password to rainyDays
    Please log in and change it at your earliest convenience.

While another use may receive the message as:

.. code-block:: text

    Subject: Important notice for billy-bo-bob

    Hello billy-bo-bob, we have reset your password to dancingOctopus
    Please log in and change it at your earliest convenience.

While the decorator plugin provides a means to solve this problem, there are
various ways you could tackle this problem without the need for a plugin.
We're trying to come up with a better way ourselves and while we have several
(obvious) ideas we don't quite have the perfect solution to go ahead and
implement it. Watch this space.

Providing Your Own Replacements Lookup for the Decorator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Filling an array with replacements may not be the best solution for providing
replacement information to the decorator. If you have a more elegant algorithm
that performs replacement lookups on-the-fly you may provide your own
implementation.

Providing your own replacements lookup implementation for the Decorator is
simply a matter of passing an instance of ``Swift_Plugins_Decorator_Replacements`` to the decorator plugin's constructor,
rather than passing in an array.

The Replacements interface is very simple to implement since it has just one
method: ``getReplacementsFor($address)``.

Imagine you want to look up replacements from a database on-the-fly, you might
provide an implementation that does this. You need to create a small class.

.. code-block:: php

    class DbReplacements implements Swift_Plugins_Decorator_Replacements {
      public function getReplacementsFor($address) {
        global $db; // Your PDO instance with a connection to your database
        $query = $db->prepare(
          "SELECT * FROM `users` WHERE `email` = ?"
        );

        $query->execute([$address]);

        if ($row = $query->fetch(PDO::FETCH_ASSOC)) {
          return array(
            '{username}'=>$row['username'],
            '{password}'=>$row['password']
          );
        }
      }
    }

Now all you need to do is pass an instance of your class into the Decorator
plugin's constructor instead of passing an array.

.. code-block:: php

    $decorator = new Swift_Plugins_DecoratorPlugin(new DbReplacements());

    $mailer->registerPlugin($decorator);

For each message sent, the plugin will call your class' ``getReplacementsFor()``
method to find the array of replacements it needs.

.. note::

    If your lookup algorithm is case sensitive, you should transform the
    ``$address`` argument as appropriate -- for example by passing it
    through ``strtolower()``.