Using PHP to create a mailing list system

Posted by: JDS

I've created a mailing list tool using PHP, Javascript for UI stuff -- love that DOM!, and Perl. In any case, this article explains the techniques I've used, problems I've had, and solutions I've employed.

Okay, so I have been tasked with making, basically, an MLM (Mailing List Manager). Well, the MLM is sorta broken into two parts -- the LIST management part, and the EMAIL creation and sending part. This article only discusses the email sending aspect.

I encountered a number of problems and solutions on this project, and I consider it a good, well-rounded example of small web application development. Small? Well, ultimately, it really is a pretty small project, but I did use a number of skills and techniquest to complete it. (Did I say "complete"? Hah! A Web application is never complete!)

Problem 1: Sending email using PHP

Description of solution needed...

Problem 2: Improving the sending of email to allow it to scale up to dozens of emails at once.

Well, the simple answer is "forking". But that turned out to be not really possible using PHP alone. PHP has some process forking tools, but they are only usable when using PHP in CLI mode. See the manual for (sketchy) details (http://us3.php.net/manual/en/ref.pcntl.php).

Okay, so PHP won't fork when running as a web server module. Or, at least, it won't work as advertised (it crashed the server when I tried it!). So I needed to create a system tool that I could then use with "exec()" or "system()" -- PHP system calls. Using those, I could put an ampersand at the end of the command and the Unix shell will do the forking. So I wrote a simple email sender in Perl, and used that. It really is quite basic! Here it is:

#!/usr/bin/perl
$TO_LIST_FILE = shift @ARGV;
$EMAIL_CONTENTS = shift @ARGV;

@to_list = get_to_list( $TO_LIST_FILE );
$message = get_message( $EMAIL_CONTENTS );

foreach $to ( @to_list ){
        $message_out = $message;
        $message_out =~ s/\@\@TO\@\@/$to/g;
        sendEmail( $message_out );
}

unlink( $TO_LIST_FILE );
unlink( $EMAIL_CONTENTS );

sub get_message{
        $out = '';
        $filename = shift @_;
        open(INFILE,  $filename)   or die "Can¿t open $filename: $!";
        while (<INFILE>){
                $out .= $_;
        }
        return $out;
}
sub get_to_list{
        $filename = shift @_;
        open(INFILE,  $filename)   or die "Can¿t open $filename: $!";
        while (<INFILE>){
                next if /^\s*$/;
                chomp;
                push @out, $_;
        }
        return @out;
}

sub sendEmail {
        # Optional: put a sleep statement here to help reduce server load.
        # sleep 1;
        my $message = shift @_;
        my $sendmail = '/usr/sbin/sendmail';
        open(MAIL, "|$sendmail -oi -t");
        print MAIL "$message\n";
        close(MAIL);
}

A couple of minor points:

  • The optional "sleep" line might help reduce server load. Make it sleep for a 60s between emails, if you like.
  • To provide a bit of security, the email message contents files are deleted immediately after being used.
  • Also, for security reasons, I have chmodded the script's permissions to be RWX only by the Web Server user ("nobody").

The Perl script takes 2 arguments: the full system path of a file containing a list of email addresses, and the full path to a file containing the preformatted email message. The message body needs to use a "mail merge" field in place of the "To:" in order for the Perl script to substitute each email address in the list. I also redirect STDER to /dev/null

Important Considerations

It is important to realize that the Perl script is so closely dependent on the PHP script that they are, in many senses, components of the same script. I just wrote one small part in Perl, is all.

Also, this combination of scripts simply will not work on a Windows platform.

Also, also, one needs to have sendmail installed (and properly configured) for the actual mailing part.

Problem 3: Adding arbitrary lists of email addresses

I have an existing database of names and email addresses (and other things). I used that. I think the details are a little outside the scope of this article, though, except a mention that the email addresses are all used by putting them into an array.

Problem 4: Adding attachments and creating HTML-formatted email

Wow, this was a doozy. But fun. There are a lot of new things about this part that I had to learn. Also, I describe how to create HTML-formatted emails in this section, as the techniques to do that are the same as those used to create email attachments. Here are some of the details.

Things I had to Learn About

  • Using the proper email headers to define different parts.
  • Using MIME headers to define related, alternative, and mixed content parts.
  • Using PHP functions to base64 encode binary content.
  • How to embed related images in-line with the HTML email (I didn't use this technique, but I learned how to do it!)
  • How to use JavaScript to create (and delete) content. For the user interface, of course.
  • Other stuff...

Of course, that's a lot of stuff. Maybe I should break this out into a separate article altogether!

Simple summary: get uploaded files, base-64 encode those files, create a properly-formatted email message including MIME attachment boundaries. Send this to mailer.

...details coming soon...

Conclusion

I still have a number of features to add. I also need to describe the list management part. Oh, if only I had time! Woe!

« Prev item - Next Item »
---------------------------------------------

Comments

No comments yet. You can be the first!

Leave comment