How do you send e-mail on a server in which there is no mail server installed? How do you redirect e-mail messages in a testing environment so they don’t go to your users? This edition of Tips & Tricks addresses these two questions, highlighting some useful tricks to redefine or redirect
PHP provides an awesome built-in feature with the
mail() function. I refer to it as “awesome” because I originally came to this language from the background of ASP and VBScript, and to successfully send an e-mail message from
an ASP script, one had to purchase a third-party COM object and successfully install and register the object on a Windows server. PHP has mail capabilities built right into the language, providing developers with a powerful and easy way to send e-mail.
Sometimes, however, whether for purposes of security (in which the server doesn’t have access to a local mail server) or debugging (mail should be trapped and not sent to users), it becomes necessary to redefine the
mail() function, or redirect it. In this edition of Tips & Tricks, we’ll explore how to do both.
There might be times in which server administrators do not wish to provide access to mail functionality. For example, they are unwilling to install sendmail, postfix, or any other mail servers. There are valid security reasons for disallowing mail servers, such as the fear of a Web server being used as a spam relay, but this lack of functionality can put a damper on Web application features. Furthermore, while applications can be written in such a way as to get around this limitation (e.g. using sockets and SMTP), there are many third-party applications and tools that rely on PHP’s
mail() function, and it is far too time consuming to rework these applications to use your own mail function. Thus, for full compatibility, it becomes necessary to hack away at PHP’s
mail() command and create your own, but, as difficult as this sounds, it’s actually quite simple to do.
To completely redefine the
mail() function, it is necessary to recompile PHP without support for the function. Afterwards, we’ll create a new
mail() function using PHP, and your applications will be none the wiser.
First, to compile PHP without
mail(), run the
configure command as normal, including all desired parameters. Then, before running
main/php_config.h. Find the line that reads:
#define HAVE_SENDMAIL 1
Comment out this line, so that it now reads:
/* #define HAVE_SENDMAIL 1 */
make install as usual.
This will essentially disable the
mail() function, and it will no longer be available to your scripts. So, our next step is to create a
mail() function at the application level.
Listing 1 shows one such example
mail() function using the PEAR::Mail package. This function implements the same exact parameters as the native PHP
mail() function to ensure compatibility with any applications that require the use of
mail(); it does not use the
$additional_parameters parameter since that is primarily used to pass additional arguments to the sendmail (or other mailer) binary. This new function should also behave in exactly the same way as the native function and all parameters passed to it should follow the rules for
mail() as defined in the PHP manual.
NOTE: Using this method, you cannot simply create a new mail() function using the PEAR::Mail “mail” driver, as this driver also utilizes PHP’s built-in
mail() function to send mail. Thus, redefining the
mail() function to use the PEAR::Mail “smtp” driver should also work for any applications that use PEAR::Mail with the mail driver. Using PEAR::Mail with the sendmail driver will not work if sendmail is not available on the system.
Now that we have defined a new
mail() function, we need to make it accessible to the applications that require it. The quickest and easiest way to do this is to use the
auto_prepend_file setting in
auto_prepend_file = /path/to/new_mail.php
You may also set this in your Apache
php_value auto_prepend_file /path/to/new_mail.php
Now, we have a
mail() function that will behave similarly to the built-in function, and all PHP applications on the system have access to use it. Keep in mind that other PHP mailing libraries could be used; you are not limited to PEAR::Mail.
At times it is preferable to turn off mail functionality altogether without recompiling PHP. This includes applications that are running on testing servers and need to use
mail() for debugging purposes, but should not send any actual mail messages—or should send messages but only to the developers. In cases such as these, it is possible to redirect mail messages sent through
mail() by modifying the
sendmail_path is a simple task. The complexities lie in the script to which all mail is redirected. This script may be as simple as directing all mail to a log file or redirecting it to the project developers, or it may be as complex as implementing a full-scale mail solution using a PHP command-line interface (CLI) script to both send mail, as illustrated in Listing 1, and log everything. We’ll examine all of these options.
If the goal is to temporarily turn off mail and redirect it to a log file, simply create a script named
logmail, set the permissions level to
chmod 755 logmail), and put the following line in the script:
cat >> /tmp/logmail.log
/path/to/logmail. Don’t forget to restart your web server. Now, all e-mail sent by applications will be stored in
/tmp/logmail.log rather than reaching the recipient in the
sendmail_path directive may be set only in
php.ini or Apache’s
httpd.conf. It cannot be set from an
There may be times, however, when properly testing an application means that all e-mail messages generated by the application must be sent somewhere, but they shouldn’t go to any real users. Thus, we need to trap the mail, which is another fairly simple task.
Create a script named
trapmail, set the permissions level, again, to
755, and place the following in the script (replacing email@example.com with your choice of e- mail address, of course):
formail -R cc X-original-cc \ -R to X-original-to \ -R bcc X-original-bcc \ -f -A”To: firstname.lastname@example.org” \ | /usr/sbin/sendmail -t –i
Then, as with earlier, set the
sendmail_path directive to
/path/to/trapmail. This will successfully redirect all e-mail messages sent by the application to email@example.com, and the original
Bcc headers will be rewritten to
To do this, sendmail must be available on the system, yet it is not required, since it is possible to create a PHP CLI script to combine this sort of redirecting with code from the custom
mail() script of Listing 1 to redirect and log any messages sent by a PHP application. Listing 2 gives a glimpse into how this is possible.
I would save the code in Listing 2 to a file such as
/usr/local/bin/php_mailer and set its permissions to
755. Then, I would implement some form of logging, perhaps using PHP 5’s file_put_contents(), along with a mailer package to send mail to either the intended recipient (on a production server) or the developers (on a testing server).
Also, notice that the mail is received on standard input in Listing 2. The message is being received in exactly the same format that sendmail would receive it. Thus, this script must parse the received message, extract the headers and body (we have already done this), and send them to PEAR::Mail in the format it expects.
sendmail_path directive must be set to
/usr/local/bin/php_mailer to make use of it.
These suggestions are simple, yet effective, ways to either send e-mail when your server can’t support PHP’s native
mail() function or you wish to redirect messages during development or testing. I hope you can see how these methods are versatile and can be extended to implement some rather complex mailing functionality.
I’d like to thank Sean Coates and Davey Shafik, who allowed me the use of content from their blogs to make this column possible.
Until next time, happy coding!