1Message Headers
2===============
3
4Sometimes you'll want to add your own headers to a message or modify/remove
5headers that are already present. You work with the message's HeaderSet to do
6this.
7
8Header Basics
9-------------
10
11All MIME entities in Swift Mailer -- including the message itself -- store
12their headers in a single object called a HeaderSet. This HeaderSet is
13retrieved with the ``getHeaders()`` method.
14
15As mentioned in the previous chapter, everything that forms a part of a message
16in Swift Mailer is a MIME entity that is represented by an instance of
17``Swift_Mime_SimpleMimeEntity``. This includes -- most notably -- the message
18object itself, attachments, MIME parts and embedded images. Each of these MIME
19entities consists of a body and a set of headers that describe the body.
20
21For all of the "standard" headers in these MIME entities, such as the
22``Content-Type``, there are named methods for working with them, such as
23``setContentType()`` and ``getContentType()``. This is because headers are a
24moderately complex area of the library. Each header has a slightly different
25required structure that it must meet in order to comply with the standards that
26govern email (and that are checked by spam blockers etc).
27
28You fetch the HeaderSet from a MIME entity like so::
29
30    $message = new Swift_Message();
31
32    // Fetch the HeaderSet from a Message object
33    $headers = $message->getHeaders();
34
35    $attachment = Swift_Attachment::fromPath('document.pdf');
36
37    // Fetch the HeaderSet from an attachment object
38    $headers = $attachment->getHeaders();
39
40The job of the HeaderSet is to contain and manage instances of Header objects.
41Depending upon the MIME entity the HeaderSet came from, the contents of the
42HeaderSet will be different, since an attachment for example has a different
43set of headers to those in a message.
44
45You can find out what the HeaderSet contains with a quick loop, dumping out the
46names of the headers::
47
48    foreach ($headers->getAll() as $header) {
49      printf("%s<br />\n", $header->getFieldName());
50    }
51
52    /*
53    Content-Transfer-Encoding
54    Content-Type
55    MIME-Version
56    Date
57    Message-ID
58    From
59    Subject
60    To
61    */
62
63You can also dump out the rendered HeaderSet by calling its ``toString()``
64method::
65
66    echo $headers->toString();
67
68    /*
69    Message-ID: <1234869991.499a9ee7f1d5e@swift.generated>
70    Date: Tue, 17 Feb 2009 22:26:31 +1100
71    Subject: Awesome subject!
72    From: sender@example.org
73    To: recipient@example.org
74    MIME-Version: 1.0
75    Content-Type: text/plain; charset=utf-8
76    Content-Transfer-Encoding: quoted-printable
77    */
78
79Where the complexity comes in is when you want to modify an existing header.
80This complexity comes from the fact that each header can be of a slightly
81different type (such as a Date header, or a header that contains email
82addresses, or a header that has key-value parameters on it!). Each header in
83the HeaderSet is an instance of ``Swift_Mime_Header``. They all have common
84functionality, but knowing exactly what type of header you're working with will
85allow you a little more control.
86
87You can determine the type of header by comparing the return value of its
88``getFieldType()`` method with the constants ``TYPE_TEXT``,
89``TYPE_PARAMETERIZED``, ``TYPE_DATE``, ``TYPE_MAILBOX``, ``TYPE_ID`` and
90``TYPE_PATH`` which are defined in ``Swift_Mime_Header``::
91
92    foreach ($headers->getAll() as $header) {
93      switch ($header->getFieldType()) {
94        case Swift_Mime_Header::TYPE_TEXT: $type = 'text';
95          break;
96        case Swift_Mime_Header::TYPE_PARAMETERIZED: $type = 'parameterized';
97          break;
98        case Swift_Mime_Header::TYPE_MAILBOX: $type = 'mailbox';
99          break;
100        case Swift_Mime_Header::TYPE_DATE: $type = 'date';
101          break;
102        case Swift_Mime_Header::TYPE_ID: $type = 'ID';
103          break;
104        case Swift_Mime_Header::TYPE_PATH: $type = 'path';
105          break;
106      }
107      printf("%s: is a %s header<br />\n", $header->getFieldName(), $type);
108    }
109
110    /*
111    Content-Transfer-Encoding: is a text header
112    Content-Type: is a parameterized header
113    MIME-Version: is a text header
114    Date: is a date header
115    Message-ID: is a ID header
116    From: is a mailbox header
117    Subject: is a text header
118    To: is a mailbox header
119    */
120
121Headers can be removed from the set, modified within the set, or added to the
122set.
123
124The following sections show you how to work with the HeaderSet and explain the
125details of each implementation of ``Swift_Mime_Header`` that may exist within
126the HeaderSet.
127
128Header Types
129------------
130
131Because all headers are modeled on different data (dates, addresses, text!)
132there are different types of Header in Swift Mailer. Swift Mailer attempts to
133categorize all possible MIME headers into more general groups, defined by a
134small number of classes.
135
136Text Headers
137~~~~~~~~~~~~
138
139Text headers are the simplest type of Header. They contain textual information
140with no special information included within it -- for example the Subject
141header in a message.
142
143There's nothing particularly interesting about a text header, though it is
144probably the one you'd opt to use if you need to add a custom header to a
145message. It represents text just like you'd think it does. If the text contains
146characters that are not permitted in a message header (such as new lines, or
147non-ascii characters) then the header takes care of encoding the text so that
148it can be used.
149
150No header -- including text headers -- in Swift Mailer is vulnerable to
151header-injection attacks. Swift Mailer breaks any attempt at header injection
152by encoding the dangerous data into a non-dangerous form.
153
154It's easy to add a new text header to a HeaderSet. You do this by calling the
155HeaderSet's ``addTextHeader()`` method::
156
157    $message = new Swift_Message();
158    $headers = $message->getHeaders();
159    $headers->addTextHeader('Your-Header-Name', 'the header value');
160
161Changing the value of an existing text header is done by calling it's
162``setValue()`` method::
163
164    $subject = $message->getHeaders()->get('Subject');
165    $subject->setValue('new subject');
166
167When output via ``toString()``, a text header produces something like the
168following::
169
170    $subject = $message->getHeaders()->get('Subject');
171    $subject->setValue('amazing subject line');
172    echo $subject->toString();
173
174    /*
175
176    Subject: amazing subject line
177
178    */
179
180If the header contains any characters that are outside of the US-ASCII range
181however, they will be encoded. This is nothing to be concerned about since mail
182clients will decode them back::
183
184    $subject = $message->getHeaders()->get('Subject');
185    $subject->setValue('contains – dash');
186    echo $subject->toString();
187
188    /*
189
190    Subject: contains =?utf-8?Q?=E2=80=93?= dash
191
192    */
193
194Parameterized Headers
195~~~~~~~~~~~~~~~~~~~~~
196
197Parameterized headers are text headers that contain key-value parameters
198following the textual content. The Content-Type header of a message is a
199parameterized header since it contains charset information after the content
200type.
201
202The parameterized header type is a special type of text header. It extends the
203text header by allowing additional information to follow it. All of the methods
204from text headers are available in addition to the methods described here.
205
206Adding a parameterized header to a HeaderSet is done by using the
207``addParameterizedHeader()`` method which takes a text value like
208``addTextHeader()`` but it also accepts an associative array of key-value
209parameters::
210
211    $message = new Swift_Message();
212    $headers = $message->getHeaders();
213    $headers->addParameterizedHeader(
214      'Header-Name', 'header value',
215      ['foo' => 'bar']
216      );
217
218To change the text value of the header, call it's ``setValue()`` method just as
219you do with text headers.
220
221To change the parameters in the header, call the header's ``setParameters()``
222method or the ``setParameter()`` method (note the pluralization)::
223
224    $type = $message->getHeaders()->get('Content-Type');
225
226    // setParameters() takes an associative array
227    $type->setParameters([
228      'name' => 'file.txt',
229      'charset' => 'iso-8859-1'
230    ]);
231
232    // setParameter() takes two args for $key and $value
233    $type->setParameter('charset', 'iso-8859-1');
234
235When output via ``toString()``, a parameterized header produces something like
236the following::
237
238    $type = $message->getHeaders()->get('Content-Type');
239    $type->setValue('text/html');
240    $type->setParameter('charset', 'utf-8');
241
242    echo $type->toString();
243
244    /*
245
246    Content-Type: text/html; charset=utf-8
247
248    */
249
250If the header contains any characters that are outside of the US-ASCII range
251however, they will be encoded, just like they are for text headers. This is
252nothing to be concerned about since mail clients will decode them back.
253Likewise, if the parameters contain any non-ascii characters they will be
254encoded so that they can be transmitted safely::
255
256    $attachment = new Swift_Attachment();
257    $disp = $attachment->getHeaders()->get('Content-Disposition');
258    $disp->setValue('attachment');
259    $disp->setParameter('filename', 'report–may.pdf');
260    echo $disp->toString();
261
262    /*
263
264    Content-Disposition: attachment; filename*=utf-8''report%E2%80%93may.pdf
265
266    */
267
268Date Headers
269~~~~~~~~~~~~
270
271Date headers contains an RFC 2822 formatted date (i.e. what PHP's ``date('r')``
272returns). They are used anywhere a date or time is needed to be presented as a
273message header.
274
275The data on which a date header is modeled as a DateTimeImmutable object. The
276object is used to create a correctly structured RFC 2822 formatted date with
277timezone such as ``Tue, 17 Feb 2009 22:26:31 +1100``.
278
279The obvious place this header type is used is in the ``Date:`` header of the
280message itself.
281
282It's easy to add a new date header to a HeaderSet. You do this by calling the
283HeaderSet's ``addDateHeader()`` method::
284
285    $message = new Swift_Message();
286    $headers = $message->getHeaders();
287    $headers->addDateHeader('Your-Header', new DateTimeImmutable('3 days ago'));
288
289Changing the value of an existing date header is done by calling it's
290``setDateTime()`` method::
291
292    $date = $message->getHeaders()->get('Date');
293    $date->setDateTime(new DateTimeImmutable());
294
295When output via ``toString()``, a date header produces something like the
296following::
297
298    $date = $message->getHeaders()->get('Date');
299    echo $date->toString();
300
301    /*
302
303    Date: Wed, 18 Feb 2009 13:35:02 +1100
304
305    */
306
307Mailbox (e-mail address) Headers
308~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
309
310Mailbox headers contain one or more email addresses, possibly with personalized
311names attached to them. The data on which they are modeled is represented by an
312associative array of email addresses and names.
313
314Mailbox headers are probably the most complex header type to understand in
315Swift Mailer because they accept their input as an array which can take various
316forms, as described in the previous chapter.
317
318All of the headers that contain e-mail addresses in a message -- with the
319exception of ``Return-Path:`` which has a stricter syntax -- use this header
320type. That is, ``To:``, ``From:`` etc.
321
322You add a new mailbox header to a HeaderSet by calling the HeaderSet's
323``addMailboxHeader()`` method::
324
325    $message = new Swift_Message();
326    $headers = $message->getHeaders();
327    $headers->addMailboxHeader('Your-Header-Name', [
328          'person1@example.org' => 'Person Name One',
329          'person2@example.org',
330          'person3@example.org',
331          'person4@example.org' => 'Another named person'
332    ]);
333
334Changing the value of an existing mailbox header is done by calling it's
335``setNameAddresses()`` method::
336
337    $to = $message->getHeaders()->get('To');
338    $to->setNameAddresses([
339      'joe@example.org' => 'Joe Bloggs',
340      'john@example.org' => 'John Doe',
341      'no-name@example.org'
342    ]);
343
344If you don't wish to concern yourself with the complicated accepted input
345formats accepted by ``setNameAddresses()`` as described in the previous chapter
346and you only want to set one or more addresses (not names) then you can just
347use the ``setAddresses()`` method instead::
348
349    $to = $message->getHeaders()->get('To');
350    $to->setAddresses([
351      'joe@example.org',
352      'john@example.org',
353      'no-name@example.org'
354    ]);
355
356.. note::
357
358    Both methods will accept the above input format in practice.
359
360If all you want to do is set a single address in the header, you can use a
361string as the input parameter to ``setAddresses()`` and/or
362``setNameAddresses()``::
363
364    $to = $message->getHeaders()->get('To');
365    $to->setAddresses('joe-bloggs@example.org');
366
367When output via ``toString()``, a mailbox header produces something like the
368following::
369
370    $to = $message->getHeaders()->get('To');
371    $to->setNameAddresses([
372      'person1@example.org' => 'Name of Person',
373      'person2@example.org',
374      'person3@example.org' => 'Another Person'
375    ]);
376
377    echo $to->toString();
378
379    /*
380
381    To: Name of Person <person1@example.org>, person2@example.org, Another Person
382     <person3@example.org>
383
384    */
385
386Internationalized domains are automatically converted to IDN encoding::
387
388    $to = $message->getHeaders()->get('To');
389    $to->setAddresses('joe@ëxämple.org');
390
391    echo $to->toString();
392
393    /*
394
395    To: joe@xn--xmple-gra1c.org
396
397    */
398
399ID Headers
400~~~~~~~~~~
401
402ID headers contain identifiers for the entity (or the message). The most
403notable ID header is the Message-ID header on the message itself.
404
405An ID that exists inside an ID header looks more-or-less less like an email
406address. For example, ``<1234955437.499becad62ec2@example.org>``. The part to
407the left of the @ sign is usually unique, based on the current time and some
408random factor. The part on the right is usually a domain name.
409
410Any ID passed to the header's ``setId()`` method absolutely MUST conform to
411this structure, otherwise you'll get an Exception thrown at you by Swift Mailer
412(a ``Swift_RfcComplianceException``). This is to ensure that the generated
413email complies with relevant RFC documents and therefore is less likely to be
414blocked as spam.
415
416It's easy to add a new ID header to a HeaderSet. You do this by calling the
417HeaderSet's ``addIdHeader()`` method::
418
419    $message = new Swift_Message();
420    $headers = $message->getHeaders();
421    $headers->addIdHeader('Your-Header-Name', '123456.unqiue@example.org');
422
423Changing the value of an existing ID header is done by calling its ``setId()``
424method::
425
426    $msgId = $message->getHeaders()->get('Message-ID');
427    $msgId->setId(time() . '.' . uniqid('thing') . '@example.org');
428
429When output via ``toString()``, an ID header produces something like the
430following::
431
432    $msgId = $message->getHeaders()->get('Message-ID');
433    echo $msgId->toString();
434
435    /*
436
437    Message-ID: <1234955437.499becad62ec2@example.org>
438
439    */
440
441Path Headers
442~~~~~~~~~~~~
443
444Path headers are like very-restricted mailbox headers. They contain a single
445email address with no associated name. The Return-Path header of a message is a
446path header.
447
448You add a new path header to a HeaderSet by calling the HeaderSet's
449``addPathHeader()`` method::
450
451    $message = new Swift_Message();
452    $headers = $message->getHeaders();
453    $headers->addPathHeader('Your-Header-Name', 'person@example.org');
454
455Changing the value of an existing path header is done by calling its
456``setAddress()`` method::
457
458    $return = $message->getHeaders()->get('Return-Path');
459    $return->setAddress('my-address@example.org');
460
461When output via ``toString()``, a path header produces something like the
462following::
463
464    $return = $message->getHeaders()->get('Return-Path');
465    $return->setAddress('person@example.org');
466    echo $return->toString();
467
468    /*
469
470    Return-Path: <person@example.org>
471
472    */
473
474Header Operations
475-----------------
476
477Working with the headers in a message involves knowing how to use the methods
478on the HeaderSet and on the individual Headers within the HeaderSet.
479
480Adding new Headers
481~~~~~~~~~~~~~~~~~~
482
483New headers can be added to the HeaderSet by using one of the provided
484``add..Header()`` methods.
485
486The added header will appear in the message when it is sent::
487
488    // Adding a custom header to a message
489    $message = new Swift_Message();
490    $headers = $message->getHeaders();
491    $headers->addTextHeader('X-Mine', 'something here');
492
493    // Adding a custom header to an attachment
494    $attachment = Swift_Attachment::fromPath('/path/to/doc.pdf');
495    $attachment->getHeaders()->addDateHeader('X-Created-Time', time());
496
497Retrieving Headers
498~~~~~~~~~~~~~~~~~~
499
500Headers are retrieved through the HeaderSet's ``get()`` and ``getAll()``
501methods::
502
503    $headers = $message->getHeaders();
504
505    // Get the To: header
506    $toHeader = $headers->get('To');
507
508    // Get all headers named "X-Foo"
509    $fooHeaders = $headers->getAll('X-Foo');
510
511    // Get the second header named "X-Foo"
512    $foo = $headers->get('X-Foo', 1);
513
514    // Get all headers that are present
515    $all = $headers->getAll();
516
517When using ``get()`` a single header is returned that matches the name (case
518insensitive) that is passed to it. When using ``getAll()`` with a header name,
519an array of headers with that name are returned. Calling ``getAll()`` with no
520arguments returns an array of all headers present in the entity.
521
522.. note::
523
524    It's valid for some headers to appear more than once in a message (e.g.
525    the Received header). For this reason ``getAll()`` exists to fetch all
526    headers with a specified name. In addition, ``get()`` accepts an optional
527    numerical index, starting from zero to specify which header you want more
528    specifically.
529
530.. note::
531
532    If you want to modify the contents of the header and you don't know for
533    sure what type of header it is then you may need to check the type by
534    calling its ``getFieldType()`` method.
535
536Check if a Header Exists
537~~~~~~~~~~~~~~~~~~~~~~~~
538
539You can check if a named header is present in a HeaderSet by calling its
540``has()`` method::
541
542    $headers = $message->getHeaders();
543
544    // Check if the To: header exists
545    if ($headers->has('To')) {
546      echo 'To: exists';
547    }
548
549    // Check if an X-Foo header exists twice (i.e. check for the 2nd one)
550    if ($headers->has('X-Foo', 1)) {
551      echo 'Second X-Foo header exists';
552    }
553
554If the header exists, ``true`` will be returned or ``false`` if not.
555
556.. note::
557
558    It's valid for some headers to appear more than once in a message (e.g.
559    the Received header). For this reason ``has()`` accepts an optional
560    numerical index, starting from zero to specify which header you want to
561    check more specifically.
562
563Removing Headers
564~~~~~~~~~~~~~~~~
565
566Removing a Header from the HeaderSet is done by calling the HeaderSet's
567``remove()`` or ``removeAll()`` methods::
568
569    $headers = $message->getHeaders();
570
571    // Remove the Subject: header
572    $headers->remove('Subject');
573
574    // Remove all X-Foo headers
575    $headers->removeAll('X-Foo');
576
577    // Remove only the second X-Foo header
578    $headers->remove('X-Foo', 1);
579
580When calling ``remove()`` a single header will be removed. When calling
581``removeAll()`` all headers with the given name will be removed. If no headers
582exist with the given name, no errors will occur.
583
584.. note::
585
586    It's valid for some headers to appear more than once in a message (e.g.
587    the Received header). For this reason ``remove()`` accepts an optional
588    numerical index, starting from zero to specify which header you want to
589    check more specifically. For the same reason, ``removeAll()`` exists to
590    remove all headers that have the given name.
591
592Modifying a Header's Content
593~~~~~~~~~~~~~~~~~~~~~~~~~~~~
594
595To change a Header's content you should know what type of header it is and then
596call it's appropriate setter method. All headers also have a
597``setFieldBodyModel()`` method that accepts a mixed parameter and delegates to
598the correct setter::
599
600The header will be updated inside the HeaderSet and the changes will be seen
601when the message is sent::
602
603    $headers = $message->getHeaders();
604
605    // Change the Subject: header
606    $subj = $headers->get('Subject');
607    $subj->setValue('new subject here');
608
609    // Change the To: header
610    $to = $headers->get('To');
611    $to->setNameAddresses([
612      'person@example.org' => 'Person',
613      'thing@example.org'
614    ]);
615
616    // Using the setFieldBodyModel() just delegates to the correct method
617    // So here to calls setNameAddresses()
618    $to->setFieldBodyModel([
619      'person@example.org' => 'Person',
620      'thing@example.org'
621    ]);
622