Qafoo GmbH - passion for software quality
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:Author: Kore Nordmann
:Date: Fri, 16 Sep 2016 11:44:49 +0200
:Revision: 5
:Copyright: All rights reserved
========================================
Apache Zeta Components: Doing mail right
========================================
:Keywords: apache zeta components, php, mail, html, attachment, image, send,
receive, smtp, imap, ez components, zeta components, tutorial,
example
:Description:
The Apache Zeta Mail component allows you to send and receive complex
emails very easily. This article shows you practical examples to see how it
works.
:Abstract:
Sending and receiving mail is a regular, but often cumbersome task.
Especially, when it comes to more complex mails than just plain text. HTML
and alternate text parts, embedded images, attachments and digests can make
your brain spin if you need to do it yourself. The Apache Zeta Mail
component makes it easy for you to send such mails, but also to receive
them! In this article I show you how to send HTML emails with embedded
images and how to receive all mails from an IMAP server and forward them in
a single mail, using a digest.
Sending and receiving mail is a regular, but often cumbersome task. Especially,
when it comes to more complex mails than just plain text. HTML and alternate
text parts, embedded images, attachments and digests can make your brain spin
if you need to do it yourself. The Apache Zeta Mail component makes it easy for
you to send such mails, but also to receive them! In this article I show you
how to send HTML emails with embedded images and how to receive all mails from
an IMAP server and forward them in a single mail, using a digest.
In following, I provide you with three more advanced examples for uses of the
Zeta Mail component. To get an insight into further possibilities and full API
docs, please refer to the `documentation of the Zeta Mail component`__. The
`example sources`__ are also provided in our Github repository, so you can grab
them easily.
__ http://incubator.apache.org/zetacomponents/documentation/trunk/Mail/tutorial.html
__ https://github.com/Qafoo/blog-examples/tree/master/zeta_mail/
---------------
Getting started
---------------
.. note::
Want to `be backed by a professional company`__ when using Zeta Components?
**Buy Qafoo blanket our per ticket support!**
__ /services/support.html
To get started with Apache Zeta Mail, you need to install (at least) the Zeta
Base component and of course the Mail component. At the current state, Apache
Zeta Components do not have an official release, yet. So you can either use the
most recent `eZ Components release`__ or check out the components from the
`Zeta SVN`__. I won't go into detail on this procedure, since it is pretty well
`documented on our website`__. Same applies to our autoload mechanism, for which
you have multiple choices to fit it into your environment.
__ http://ezcomponents.org/
__ http://svn.apache.org/repos/asf/incubator/zetacomponents/trunk
__ http://incubator.apache.org/zetacomponents/documentation/install.html
So, let's get straight to the point …
------------------
Sending HTML email
------------------
Using the Zeta Mail component you can send email composed of almost arbitrary
MIME parts, so sending HTML email with embedded images is an easy task::
if ( !isset( $argv[1] ) )
{
die( "Missing image file to send.\n" );
}
if ( ( $imagePath = realpath( $argv[1] ) ) === false )
{
die( "Image file '{$argv[1]}' not found.\n" );
}
require_once 'Base/src/ezc_bootstrap.php';
$mail = new ezcMail();
$mail->from = new ezcMailAddress(
'somebody@example.com',
'Some Body'
);
$mail->addTo(
new ezcMailAddress(
'anybody@example.com',
'Any Body'
)
);
$mail->subject = 'A pic from Some Body';
$textPart = new ezcMailText(
'This email contains HTML content.'
. 'Please enable viewing HTML to fully enjoy it.'
);
$htmlPart = new ezcMailText(
''
. '
Some Body wants you to see this image
'
. '
. ''
);
$htmlPart->subType = 'html';
$imagePart = new ezcMailFile( $imagePath );
$imagePart->contentId = 'included_image';
$mail->body = new ezcMailMultipartAlternative(
$textPart,
new ezcMailMultipartRelated(
$htmlPart,
$imagePart
)
);
$transport = new ezcMailMtaTransport();
$transport->send( $mail );
This is a simple script you could use on your server to get uploaded images
sent to you as HTML mails. After some checks for the shell parameter, the Zeta
Components autoload is included using our bootstrap file.
Now an instance of ``ezcMail`` is created, which represents an email. Of course
an email needs a ``from`` field. Email addresses are covered in instances of
``ezcMailAddress`` in order to specify the address itself and an optional name.
the ``ezcMail::addTo()`` method can be called multiple times in order to add
recipients (of course ``addCc()`` and ``addBcc()`` exist, too). The subject is
defined intuitively.
.. note::
Do you **miss a feature in Apache Zeta Components**? Qafoo experts offer you
`payed development at fair rates`__!
__ /services/support.html#open-source-sponsoring
So, if you are sending HTML email, it is good practice to at least include
alternative plain text content which is displayed by the email client if it
cannot handle HTML email or if the user simply switched it off. Therefore, an
``ezcMailText`` object is created first. The ``ezcMailText`` class (as the
``ezcMail`` class itself) extends ``ezcMailPart`` and can therefore be used to
compose email bodies.
HTML is nothing really more than plain text, but a corresponding MIME type must
be indicated in the email source. Therefore the ``$htmlPart`` needs the
``subType`` property to be set accordingly. To include an image from a file,
you need a ``ezcMailFile`` part. If you now want to reference this part in the
HTML part, you need to give it a ``contentId`` (*CID*). You know with IDs they
may be somehow *arbitrary*, but excluding white spaces and special chars is a
good idea. The HTML part already used the CID of the included image in the
``src`` attribute of the ``img`` tag to reference it.
Now all elements of the desired email have been created and the body can be
composed: Since plain text and html parts should be alternatives, an
``ezcMailMultipartAlternative`` part is used to wrap them in the body. The HTML
part is in addition related to the file part (the image) and is therefore put
into an according container to reflect this.
Now the ``ezcMail`` object is ready for sending. In order to do this, you need
an instance of ``ezcMailTransport``. In the example, ``ezcMailMtaTransport`` is
used, which sends mails using PHPs ``mail()`` function. Alternatively, you
could connect to an SMTP server directly, using the ``ezcMailSmtpTransport``.
The latter one uses PHPs socket API for connection, not an SMTP extension.
You see, sending HTML email with embedded images can be so easy …
--------------
Receiving mail
--------------
While there are many mail sending components for PHP out there, libraries to
retrieve and parse mail are no that common. The Zeta Mail component allows you
to do both. So, maybe you remember the times before smart phones, when you were
lucky to have your phone support email? The following script reads all mails
from an IMAP inbox and creates imaginary blog entries from them::
require_once 'Base/src/ezc_bootstrap.php';
require_once 'blog_entry.php';
require_once 'blog_entry_creator.php';
$imapOptions = new ezcMailImapTransportOptions();
$imapOptions->ssl = true;
$imap = new ezcMailImapTransport(
'example.com',
993,
$imapOptions
);
$imap->authenticate( 'somebody@example.com', 'foo23bar' );
$imap->selectMailbox( 'Inbox' );
$messageSet = $imap->fetchAll();
$parser = new ezcMailParser();
$mails = $parser->parseMail( $messageSet );
$blogEntryCreator = new qaBlogEntryCreator();
foreach ( $mails as $mail )
{
$entry = $blogEntryCreator->createEntry( $mail );
$entry->save();
}
The initial two ``require_once`` statements include custom classes needed in
the example. These are shown below, as they are needed. In order to connect to
an IMAP server with SSL encryption an options object is first created and the
corresponding option is set. After that, an instance of
``ezcMailImapTransport`` is used for the actual connection. This object
communicates with the server through sockets and therefore does not need any
special extension. You see some of its possibilities in action in the example,
but there are far more. Look them up in the `IMAP Transport documentation`__
when you need them.
__ http://incubator.apache.org/zetacomponents/documentation/trunk/Mail/phpdoc/ezcMailImapTransport.html
.. note::
Do you want you and your colleagues to become **experts in using Zeta
Components**? Qafoo offers you a `highly customized training`__!
__ /services/training.html
Of course you need to authenticate against the server and select a mailbox to
browse. The ``fetchAll()`` method of the IMAP transport returns a so-called
mail set object, which abstracts reading mails from IMAP. You can create
similar sets from e.g. mbox files. The ``ezcMailParser`` object is responsible
for parsing such a set of mails into ``ezcMail`` objects. Exactly, they return
you the very same data structure you use for creating mails, isn't that cute?
You can see what more you can do with this in the next example.
After parsing the mails into an array of ``ezcMail`` instances, we use the
custom ``qaBlogEntryCreator`` class to process them::
class qaBlogEntryCreator
{
protected $entry;
public function createEntry( ezcMail $mail )
{
$this->entry = new qaBlogEntry();
$this->entry->setSubject( $mail->subject );
$walkContext = new ezcMailPartWalkContext(
array( $this, 'walkPart' )
);
$walkContext->filter = array( 'ezcMailText', 'ezcMailFile' );
$mail->walkParts( $walkContext, $mail );
return $this->entry;
}
public function walkPart( ezcMailPartWalkContext $context, ezcMailPart $part )
{
switch ( true )
{
case ( $part instanceof ezcMailFile ):
$this->entry->addImage( $part->contentId, $part->fileName );
break;
case ( $part instanceof ezcMailText ):
if ( $part->subType === 'html' )
{
$this->entry->setContent( $part->text );
}
break;
}
}
}
The ``createEntry()`` method is used in the example to create a blog entry from
a mail object. It creates a new, rudimentary blog entry object and sets the
subject for the blog entry from the emails subject.
After that, a so-called *walk context* is created. This context determines a
callback to which processing of a mail part (remember mail parts from the last
example?) is delegated while the parts of a mail are walked recursively. In
addition to that, the ``$walkContext`` is provided with a filter, determining a
whitelist for interesting mail parts. For the blog entry we need an HTML
content part and potential files. Walking the mail parts recursively is done by
calling the mails ``walkParts()`` method with the created context and a root
part to start with.
.. note::
Do you want to `learn professional object oriented design`__? **Rent a Qafoo
expert for your training.**
__ /services/training.html
During the walk, the ``qaBlogEntryCreator->walkPart()`` method is called for
every ``ezcMailText`` and ``ezcMailFile`` part. Inside this method, files are
added as images to the blog entry, using their content ID and file path. The
attached files do already reside on your disk at this stage, so file path
points into a temp dir (specified through options or system default). The
content ID is needed to replace the ``
`` parts of the HTML
content in reverse order to what you saw in the first example. The HTML content
can simply be extracted.
----------------
Sending a digest
----------------
The last example in this blog entry shows how to combine receiving and sending
email using the Zeta Mail component. Imagine you have a dedicated mail box for
support issues, which is maintained by some of your colleagues. You don't want
to be bothered by every single mail, but since you are curious, you want to
receive a digest with all mails received during the day in the evening. Nothing
easier than that::
require_once 'Base/src/ezc_bootstrap.php';
$imapOptions = new ezcMailImapTransportOptions();
$imapOptions->ssl = true;
$imap = new ezcMailImapTransport(
'example.com',
993,
$imapOptions
);
$imap->authenticate( 'somebody@example.com', 'foo23bar' );
$imap->selectMailbox( 'Inbox' );
$mailSet = $imap->fetchAll();
$parser = new ezcMailParser();
$retMails = $parser->parseMail( $mailSet );
$mail = new ezcMail();
$mail->from = new ezcMailAddress( 'somebody@example.com' );
$mail->addTo(
new ezcMailAddress( 'anybody@example.com', 'Any Body' )
);
$mail->subject = 'Daily digest';
$digest = new ezcMailMultipartDigest();
foreach ( $retMails as $retMail )
{
$digest->appendPart( new ezcMailRfc822Digest( $retMail ) );
}
$mail->body = $digest;
$transport = new ezcMailMtaTransport();
$transport->send( $mail );
The first part is already known from the last example:
1. Connect to the IMAP server
2. authenticate and select the mailbox
3. receive and parse the mails.
In order to send a digest, you of course need a new ``ezcMail`` object with
corresponding ``from``, *to* and ``subject`` information. However, this email
does not contain a plain text, HTML or combined body, but a digest. This
digest is represented as an instance of ``ezcMailMultipartDigest``. To this
base body part, all the retrieved mails are then added as digest parts
(``ezcMailRfc822Digest``) and the part is simply set as the body of the mail.
Ready to go, the mail is sent in the same way is seen in example 1.
----------
Conclusion
----------
I hope you saw that the `Zeta Mail component`__ is a very powerful tool for
sending and receiving mails. If you are in the need of sending or processing
mails the next time, just give it a try.
__ http://incubator.apache.org/zetacomponents/documentation/trunk/Mail/tutorial.html
Of course, if you are keen on mail and have a cool idea for the component or
miss a feature, please join us in the `Apache Zeta Components`__ project over
at the `Apache foundation`__!
__ http://incubator.apache.org/zetacomponents/
__ http://apache.org
..
Local Variables:
mode: rst
fill-column: 79
End:
vim: et syn=rst tw=79
Trackbacks
==========
Comments
========
- Stephen S. Musoke at Mon, 29 Nov 2010 05:55:48 +0100
How does the ZetaMail compare to PHPMailer and SwiftMailer, similar
examples:
- PHPMailer - http://phpmailer.worxware.com/index.php?pg=examplebmail
- Swift Mailer - http://swiftmailer.org/docs/message-quickref
- Tobias Schlitt at Wed, 01 Dec 2010 10:52:57 +0100
I don't know much about neither of them. However, I had a quick look at the
code of both of them.
PHPMailer looks much more monolithic and inflexible than Zeta Mail. Zeta
Mail follows OOP principles quite good, while PHPMailer mainly consists of 3
huge classes (the main class source file is ~73kb). In addition, PHPMailer
seems to miss mail receiving and parsing functionality, as far as I could
see.
The Swift Mailer code seems to be more flexible than PHPMailer one. It
appears to me that it supports a huge lot of features around mail sending,
but mail receiving and parsing is not supported. Furthermore I'm not sure
how easy it would be to extend the library for additional MIME parts like
digest.
Please correct me if I'm wrong.
Regards, Toby
- Stephen S. Musoke at Thu, 02 Dec 2010 15:35:29 +0100
I now see what you are saying
There seems to be a need to create many object instances, which while
elegant will definitely make the library seem daunting to new users and
adopters
Is there a way of having some of the classes hidden from the developer, via
public setters/getters that abstract the details
For example when adding an address in PHP Mailer, you provide an email and a
name (optional), the ezcMailAddress instance can be created internally
I think Zend Framework has this kind of abstraction where you can provide an
array of options (including) a class name and the object classes are
initialized for the user
Your thoughts?
- Tobias Schlitt at Thu, 02 Dec 2010 15:47:10 +0100
Hi Stephen,
if you don't want to deal with all the internal stuff, we have the
ezcMailComposer, which provides a convenience facade for the most common
cases in a single object. That is: Simple text mail, HTML mail, HTML / Text
alternative, Attachements. You only need to deal with very few objects. Find
an example here:
http://incubator.apache.org/zetacomponents/documentation/trunk/Mail/tutorial.html#sending-a-mail-with-the-composer
However, if you need to build more complex mail, you still can do it. And I
think that's the more important point here.
We do not make use of classical getters and setters, but instead use
overloading to allow you simple $foo->bar access to properties. Therefore we
wrap the email address into a dedicated object. I personally don't consider
its usage daunting, do you?
I'm not sure what you mean with your last sentence. It somewhat sounds like
a dependency injection container (DIC). Did I get it wrong? If now, we don't
have a DIC in Zeta Components. Our way of handling configuration of
components are option classes. You can create an instance of an option
class, which validates the configuration values you set, and then submit it
to the constructor of the class you want to instantiate. This mechanism can
of course be used to integrate Zeta Components into a DIC.
HTH, Toby
- Stephen S. Musoke at Fri, 03 Dec 2010 09:47:14 +0100
The composer is less daunting than the example in the blog above.
Under normal conditions its easy to digest a lot of code, but my experience
has been that with newer developers or when searching for a solution under
pressure, the more code there is, the less you an understand.
Thank you for taking the time to clarify the issues, and Keep up the
excellent job that you are doing.
- Tobias Schlitt at Fri, 03 Dec 2010 09:49:14 +0100
Yeah, I understand that issue. However, it's not easy to provide both,
flexibility to do all the nifty stuff and an easy way to get the most wanted
stuff done. However, we try. :)
If you want to, please subscribe to the Zeta developer list
(zeta-dev@incubator.apache.org) to give us further feedback where we could
offer easier ways.
Thanks for your feedback!