Springub | November 30, 2006 at 19:20 · Filed under PHP, Development
I guess many developers could have the same challenge: Their customers want to send out a newsletter, but they don’t need an enterprise newsletter system because they send only to about 100 - 5.000 recipients. Futhermore, they wanna send it through their normal CMS application.
Here are some tips about writing a solid solution for sending a newsletter, which fit into the most requirements:
//Use a Framwork
Use a completed Framework to create and send mails, not the PHP mail function. For example: PEAR::Mail, eZ Components, Zend Framework.
You don’t have to care about encodings, escaping etc. then. This is also a security aspect as it could be possible that some evil person injects something into your headers!
//Queue in the database
Create a queue table in your database. Put every single mail with full headers and full text in a row. I know this produces redundant data, but with this you have a complete logging and solid queue solution without big trouble. Here is our SQL:
CREATE TABLE `newsletter_queue` (
`newsletterID` bigint(20) NOT NULL auto_increment,
`create_time` datetime NOT NULL default '0000-00-00 00:00:00',
`time_to_send` datetime NOT NULL default '0000-00-00 00:00:00',
`sent_time` datetime default NULL,
`newsletterreceipientID` bigint(20) NOT NULL default '0',
`newsletterjobID` int(11) default NULL,
`sender` varchar(100) NOT NULL default '',
`recipient` varchar(100) NOT NULL default '',
`headers` text NOT NULL,
`body_txt` mediumtext NOT NULL,
`body_html` mediumtext,
`try_sent` tinyint(4) NOT NULL default '0',
PRIMARY KEY (`newsletteID`),
KEY `newsletterjobID` (`newsletterjobID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
So you have a complete queue, you can interrupt and delete the queue if something goes wrong. Let some background daemon execute the jobs.
For archiving, use something like the MySQL MERGE storage engine.
//Use SMTP
It is a good idea to use the same SMTP server that the customer uses to send out his normal emails, especially when you set SPF Records. Look at your Return-Path and From: headers so that they match the sender domain.
//Check Headers
From time to time I notice that the headers are not complete. So make sure that the Date: and the To: field are in the appropriate format (in PHP: date(r);).
// Test it
Make a test with several MUAs and anti-spam solutions like the popular SpamAssassin and check if they complain about some wrong headers and /or keywords.
We hope these hints are useful for you. Comments, additions and corrections are always appreciated.
Soenke Ruempler | November 18, 2006 at 14:39 · Filed under PHP, Development
The principle of least knowledge and duck typing tell us what programming against interfaces is all about. One of my first challenges is to design a new transport layer for our website deployment system. The old system just allowed deployment via FTP (PEAR FTP Class). This was slow and had no good error checking.
The new system must be very stable with a strict error checking and has to support several ways of file transports (FTP, SCP/SFTP). SCP is faster then FTP and secure by nature. It’s going to be the standard way for deployment.
So I looked for a rsync/scp like pecl extension and found ssh2 developed by Sara Golemon, a well known PHP core developer. It supports PHP stream wrappers - cool!
A word about stream wrappers: You can use common file system functions like read/write/copy/dirlisting over any connection that supports the stream wrappers. Some examples are HTTP and FTP, for further info consider looking at the manual.
So I get excatly what i searched for. An abstraction layer for the deployment system without the need of userland third party libraries. The underlying architecture doesn’t matter at all. I wrote a little POC-script that copies a whole directory over an scp layer:
$session = ssh2_connect('localhost', 22);
ssh2_auth_password($session, 'xxx', 'xxx');
$sftp = ssh2_sftp($session);
$remoteDir = 'ssh2.sftp://' . $sftp;
$localDir = '/home/soenke/webs/soenke-dev/cms/webseitendaten';
$remoteDir .= '/home/soenke/tests';
$it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($localDir), RecursiveIteratorIterator::SELF_FIRST);
$bytes = 0;
$time = microtime(true);
foreach ($it as $e) {
$remoteFile = $remoteDir . substr($e->getPathname(), strlen($localDir));
echo '.';
if ($e->isDir() && !is_dir($remoteFile)) {
mkdir($remoteFile, 0775);
}
if ($e->isFile()) {
copy($e->getPathname(), $remoteFile);
$bytes += filesize($e->getPathname());
}
}
$time = microtime(true) - $time;
echo "n";
printf('Size: %01.2f MB - time: %01.2f secs' . "n", $bytes / (1024 * 1024), $time);
Wow! That’s the whole script. The SPL helped me for copying the whole directory. And now switching to FTP? No problem, I just have to change the code that generates the destionaion directory and the following code works without any change. This is being outsourced into a Transport_Strategy Class (with _SFTP and _FTP subclasses) which is reponsible for setting up the connection.
I also tested the remote directory listing via SPL DirectoryIterator. It works but I got a warning, asked the guys from ircnet#php.de but noone had any clue, too. So I filed a bug report. But, as said before, it works though the warning.
I hope my english is not as bad so nobody understands this post *g. Comments are appreciated.