Current e-mail handling in PHP is dreadful. Almost every framework
implements it's own email handling class in order to make sending e-mail
reliable, easy to configure, and to implement multiple mail schemes.
This means for developers working on multiple frameworks, they can't
just call mail($recipient, $subject, $message) - they have to perform
convoluted checking to find out what framework they are using, and then
use the appropriate api.
That was understandable in PHP v4 and in v5 - 5.2.
Today though it is a waste of time, PHP internally has implemented a
smart, extensible system for generalizing access to system oriented
structures. Handlers/Wrappers.
By subclassing the appropriate handler class, a PHP application can
implement specific functionality to handle specific elements of Session
and Stream processing, without having to implement every one of them and
without requiring specialized PHP classes to be used directly.
http://www.php.net/manual/en/function.session-set-save-handler.php
http://www.php.net/manual/en/function.stream-wrapper-register.php
http://www.php.net/manual/en/class.sessionhandler.php
http://www.php.net/manual/en/class.streamwrapper.php
My proposal[which I am willing to work on coding] is to replace the
current mail()
function with one that will support handlers.
Note: This is only to handle HALF of the issues that we deal with when
sending e-mail: sending the actual message. It does not attempt to
address creating/formatting e-mail messages except in a very general
since. My proposed class would be:
class MailMessage {
/* Properties */
$private array $recipients;
$private string $sender;
$private string $subject;
$private string $body;
$private string $header;
$private string $additionalParamters;
$private array $sentTo;
// Mail handler used to send mail, if none specified the current default
handler is used
$private MailHandler $handler;
/* Primary Methods */
/* create a new mail message, constructor parameters mimic current
mail()
function parameters. Throws exception if there is a problem.
It is perfectly valid to create a message and not specify any of the
parameters - it is only when the message is SENT that the parameters
must be valid */
public MailMessage __construct ( [string $to, string $subject, string
$message, string $additional_headers, string $additional_parameters,
string $from, MailHandlerInterface $handler] )
/* Send a mail message. Throws exception on problems. If $handler is
specified, then that handler is used. If not specified, then the the
objects $handler property is used. */
public MailMessage send ([MailHandle $handler ]);
/* Utility Methods */
/* Sets appropriate private properties, checks parameters and
throwsexceptions when not properly formatted */
public MailMessage setRecipients ( array $recipients )
public MailMessage setSender ( string $sender );
public MailMessage setSubject ( string $$subject );
public MailMessage setBody ( string $body );
public MailMessage setHeader ( string $header );
public MailMessage setAdditionalParameters ( string $additionalParameters );
public MailMessage setHandler( MailHandlerInterface $handler );
public MailMessage setSentTo();
/* Magic method to allow direct setting of sender, subject, body,
recipients, and additionalParameters. It is not recommended to
allow the setting of mail handler or sentTo in this manner. */
public MailMessage __set();
/* Gets appropriate private properties and returns them
either as strings or as arrays */
public mixed getRecipients([string $format = 'string']);
public mixed getSubject([string $format = 'string']);
public mixed getBody([string $format = 'string']);
public mixed getHeader([string $format = 'string']);
public mixed getAdditionalParameters([string $format = 'string']);
public MailHandlerInterface getHandler();
public array getSentTo();
/* Magic get method for sender, subject, body, recipients,
and additionalParameters. It is not recommended to allow retrieval
of the mail handler in this manner. Always returns data as an
associative array. */
public array __get();
}
Sending e-mail in an object oriented manner can be as simple as:
$message = new MailMessage($to, $subject, $message);
$message->send;
Or simply continue to use mail($to, $subject, $message) and it will
handle the above transparently.
In addition to a MailMessage class, there would also be a MailHandler
interface. It only requires 2 methods, send and sendBatch.
MailHandlerInterface {
/* Methods */
abstract public bool send ( MailMessage $message [, string $address ] )
abstract public bool sendBatch ( MailMessage $message [, array
$recipients ] )
}
sendBatch at it's simplest would simply loop through each recipient in
the message and call send($message, $recipient). It could also be
called to send mail only to specific recipients. Handlers are free to
use other logic, for example if using Postfix's maildrop function, the
message can be saved as a file to a specific directory and Postfix
monitors the directory and send's e-mail from it: no need to make costly
network connections for every recipient.
send is used to deliver a message to a single recipient. If a message
has multiple potential recipients, send should throw an exception if the
specific recipient is not specified via $address.
The send and sendBatch function CAN be used to send a message to an
address that is not listed as a recipient. IE this method can be used
for transparently copying others for every message the handler sends out.
Both send and sendBatch must update the sentTo property of the message
to add each recipient a message is actually sent to. This allows mass
e-mail situations to add tracking information to embedded images in the
message for each recipient. Note, such functionality is beyond the
scope of the base interface.
Both send and sendBatch MAY check the sentTo property and avoid sending
duplicate copies of a message but this is NOT required.
Lastly, this would require an additional function:
bool mail_handler_register ( MailHandlerInterface $handler [, int $flags
= 0 ] )
My goal is that the creation and sending of basic e-mail should be a
simple operation. It should be possible for a developer to simply call
the mail()
function and not be concerned about possible e-mail handling
configurations.
At the same time, Application Designers and Framework designers should
be able to augment basic e-mail delivery with their own custom handling
rules. Adding the above functionality to PHP would allow for easy
migration - in most cases all they would have to do is add the send and
sendBatch methods to their current mail class and make sure to flag the
class as implementing the MailHandlerInterface. Then it can be
registered to deal with sending or not.
Possible additions to the above would be:
A MailHandlerClass which can be extended. This way a handler that only
wanted to customize the send or sendBatch method could simply inherit
the base methods and customize.
Adding a createMessage method to the MailHandlerInterface so that the
message object that is created by mail($to, $subject, $message) can
include custom functionality.
Adding a mail_create() function which would return a MailMessage object
using the createMessage method from the currently registered
MailHandlerInterface.
Feedback on changes to the classes to bring things in line with current
standards is appreciated. In addition, pointers to the files which
currently implement the handler functionality in PHP would be
appreciated. I believe I found the functionality, but as I read the
code it seems that SessionHandlers and StreamHandlers do not inherit any
common code, ie each one is coded individually - so if there is a
generic mechanism for registering/using handlers already in the code I
am missing I'd rather extend that then write new code. :-)
-Gary